1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/forms/nsFileControlFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,328 @@ 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 "nsFileControlFrame.h" 1.10 + 1.11 +#include "nsGkAtoms.h" 1.12 +#include "nsCOMPtr.h" 1.13 +#include "nsIDocument.h" 1.14 +#include "nsINodeInfo.h" 1.15 +#include "mozilla/dom/Element.h" 1.16 +#include "mozilla/dom/DataTransfer.h" 1.17 +#include "mozilla/dom/HTMLButtonElement.h" 1.18 +#include "mozilla/dom/HTMLInputElement.h" 1.19 +#include "nsNodeInfoManager.h" 1.20 +#include "nsContentCreatorFunctions.h" 1.21 +#include "nsContentUtils.h" 1.22 +#include "mozilla/EventStates.h" 1.23 +#include "mozilla/dom/DOMStringList.h" 1.24 +#include "nsIDOMDragEvent.h" 1.25 +#include "nsIDOMFileList.h" 1.26 +#include "nsContentList.h" 1.27 +#include "nsIDOMMutationEvent.h" 1.28 +#include "nsTextNode.h" 1.29 + 1.30 +using namespace mozilla; 1.31 +using namespace mozilla::dom; 1.32 + 1.33 +nsIFrame* 1.34 +NS_NewFileControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) 1.35 +{ 1.36 + return new (aPresShell) nsFileControlFrame(aContext); 1.37 +} 1.38 + 1.39 +NS_IMPL_FRAMEARENA_HELPERS(nsFileControlFrame) 1.40 + 1.41 +nsFileControlFrame::nsFileControlFrame(nsStyleContext* aContext) 1.42 + : nsBlockFrame(aContext) 1.43 +{ 1.44 + AddStateBits(NS_BLOCK_FLOAT_MGR); 1.45 +} 1.46 + 1.47 + 1.48 +void 1.49 +nsFileControlFrame::Init(nsIContent* aContent, 1.50 + nsIFrame* aParent, 1.51 + nsIFrame* aPrevInFlow) 1.52 +{ 1.53 + nsBlockFrame::Init(aContent, aParent, aPrevInFlow); 1.54 + 1.55 + mMouseListener = new DnDListener(this); 1.56 +} 1.57 + 1.58 +void 1.59 +nsFileControlFrame::DestroyFrom(nsIFrame* aDestructRoot) 1.60 +{ 1.61 + ENSURE_TRUE(mContent); 1.62 + 1.63 + // Remove the events. 1.64 + if (mContent) { 1.65 + mContent->RemoveSystemEventListener(NS_LITERAL_STRING("drop"), 1.66 + mMouseListener, false); 1.67 + mContent->RemoveSystemEventListener(NS_LITERAL_STRING("dragover"), 1.68 + mMouseListener, false); 1.69 + } 1.70 + 1.71 + nsContentUtils::DestroyAnonymousContent(&mTextContent); 1.72 + nsContentUtils::DestroyAnonymousContent(&mBrowse); 1.73 + 1.74 + mMouseListener->ForgetFrame(); 1.75 + nsBlockFrame::DestroyFrom(aDestructRoot); 1.76 +} 1.77 + 1.78 +nsresult 1.79 +nsFileControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements) 1.80 +{ 1.81 + nsCOMPtr<nsIDocument> doc = mContent->GetDocument(); 1.82 + 1.83 + // Create and setup the file picking button. 1.84 + mBrowse = doc->CreateHTMLElement(nsGkAtoms::button); 1.85 + // NOTE: SetIsNativeAnonymousRoot() has to be called before setting any 1.86 + // attribute. 1.87 + mBrowse->SetIsNativeAnonymousRoot(); 1.88 + mBrowse->SetAttr(kNameSpaceID_None, nsGkAtoms::type, 1.89 + NS_LITERAL_STRING("button"), false); 1.90 + 1.91 + // Set the file picking button text depending on the current locale. 1.92 + nsXPIDLString buttonTxt; 1.93 + nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES, 1.94 + "Browse", buttonTxt); 1.95 + 1.96 + // Set the browse button text. It's a bit of a pain to do because we want to 1.97 + // make sure we are not notifying. 1.98 + nsRefPtr<nsTextNode> textContent = 1.99 + new nsTextNode(mBrowse->NodeInfo()->NodeInfoManager()); 1.100 + 1.101 + textContent->SetText(buttonTxt, false); 1.102 + 1.103 + nsresult rv = mBrowse->AppendChildTo(textContent, false); 1.104 + NS_ENSURE_SUCCESS(rv, rv); 1.105 + 1.106 + // Make sure access key and tab order for the element actually redirect to the 1.107 + // file picking button. 1.108 + nsRefPtr<HTMLInputElement> fileContent = HTMLInputElement::FromContentOrNull(mContent); 1.109 + nsRefPtr<HTMLButtonElement> browseControl = HTMLButtonElement::FromContentOrNull(mBrowse); 1.110 + 1.111 + nsAutoString accessKey; 1.112 + fileContent->GetAccessKey(accessKey); 1.113 + browseControl->SetAccessKey(accessKey); 1.114 + 1.115 + int32_t tabIndex; 1.116 + fileContent->GetTabIndex(&tabIndex); 1.117 + browseControl->SetTabIndex(tabIndex); 1.118 + 1.119 + if (!aElements.AppendElement(mBrowse)) { 1.120 + return NS_ERROR_OUT_OF_MEMORY; 1.121 + } 1.122 + 1.123 + // Create and setup the text showing the selected files. 1.124 + nsCOMPtr<nsINodeInfo> nodeInfo; 1.125 + nodeInfo = doc->NodeInfoManager()->GetNodeInfo(nsGkAtoms::label, nullptr, 1.126 + kNameSpaceID_XUL, 1.127 + nsIDOMNode::ELEMENT_NODE); 1.128 + NS_TrustedNewXULElement(getter_AddRefs(mTextContent), nodeInfo.forget()); 1.129 + // NOTE: SetIsNativeAnonymousRoot() has to be called before setting any 1.130 + // attribute. 1.131 + mTextContent->SetIsNativeAnonymousRoot(); 1.132 + mTextContent->SetAttr(kNameSpaceID_None, nsGkAtoms::crop, 1.133 + NS_LITERAL_STRING("center"), false); 1.134 + 1.135 + // Update the displayed text to reflect the current element's value. 1.136 + nsAutoString value; 1.137 + HTMLInputElement::FromContent(mContent)->GetDisplayFileName(value); 1.138 + UpdateDisplayedValue(value, false); 1.139 + 1.140 + if (!aElements.AppendElement(mTextContent)) { 1.141 + return NS_ERROR_OUT_OF_MEMORY; 1.142 + } 1.143 + 1.144 + // We should be able to interact with the element by doing drag and drop. 1.145 + mContent->AddSystemEventListener(NS_LITERAL_STRING("drop"), 1.146 + mMouseListener, false); 1.147 + mContent->AddSystemEventListener(NS_LITERAL_STRING("dragover"), 1.148 + mMouseListener, false); 1.149 + 1.150 + SyncDisabledState(); 1.151 + 1.152 + return NS_OK; 1.153 +} 1.154 + 1.155 +void 1.156 +nsFileControlFrame::AppendAnonymousContentTo(nsBaseContentList& aElements, 1.157 + uint32_t aFilter) 1.158 +{ 1.159 + aElements.MaybeAppendElement(mBrowse); 1.160 + aElements.MaybeAppendElement(mTextContent); 1.161 +} 1.162 + 1.163 +NS_QUERYFRAME_HEAD(nsFileControlFrame) 1.164 + NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator) 1.165 + NS_QUERYFRAME_ENTRY(nsIFormControlFrame) 1.166 +NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame) 1.167 + 1.168 +void 1.169 +nsFileControlFrame::SetFocus(bool aOn, bool aRepaint) 1.170 +{ 1.171 +} 1.172 + 1.173 +/** 1.174 + * This is called when we receive a drop or a dragover. 1.175 + */ 1.176 +NS_IMETHODIMP 1.177 +nsFileControlFrame::DnDListener::HandleEvent(nsIDOMEvent* aEvent) 1.178 +{ 1.179 + NS_ASSERTION(mFrame, "We should have been unregistered"); 1.180 + 1.181 + bool defaultPrevented = false; 1.182 + aEvent->GetDefaultPrevented(&defaultPrevented); 1.183 + if (defaultPrevented) { 1.184 + return NS_OK; 1.185 + } 1.186 + 1.187 + nsCOMPtr<nsIDOMDragEvent> dragEvent = do_QueryInterface(aEvent); 1.188 + if (!dragEvent || !IsValidDropData(dragEvent)) { 1.189 + return NS_OK; 1.190 + } 1.191 + 1.192 + nsAutoString eventType; 1.193 + aEvent->GetType(eventType); 1.194 + if (eventType.EqualsLiteral("dragover")) { 1.195 + // Prevent default if we can accept this drag data 1.196 + aEvent->PreventDefault(); 1.197 + return NS_OK; 1.198 + } 1.199 + 1.200 + if (eventType.EqualsLiteral("drop")) { 1.201 + aEvent->StopPropagation(); 1.202 + aEvent->PreventDefault(); 1.203 + 1.204 + nsIContent* content = mFrame->GetContent(); 1.205 + NS_ASSERTION(content, "The frame has no content???"); 1.206 + 1.207 + HTMLInputElement* inputElement = HTMLInputElement::FromContent(content); 1.208 + NS_ASSERTION(inputElement, "No input element for this file upload control frame!"); 1.209 + 1.210 + nsCOMPtr<nsIDOMDataTransfer> dataTransfer; 1.211 + dragEvent->GetDataTransfer(getter_AddRefs(dataTransfer)); 1.212 + 1.213 + nsCOMPtr<nsIDOMFileList> fileList; 1.214 + dataTransfer->GetFiles(getter_AddRefs(fileList)); 1.215 + 1.216 + inputElement->SetFiles(fileList, true); 1.217 + nsContentUtils::DispatchTrustedEvent(content->OwnerDoc(), content, 1.218 + NS_LITERAL_STRING("change"), true, 1.219 + false); 1.220 + } 1.221 + 1.222 + return NS_OK; 1.223 +} 1.224 + 1.225 +/* static */ bool 1.226 +nsFileControlFrame::DnDListener::IsValidDropData(nsIDOMDragEvent* aEvent) 1.227 +{ 1.228 + nsCOMPtr<nsIDOMDataTransfer> domDataTransfer; 1.229 + aEvent->GetDataTransfer(getter_AddRefs(domDataTransfer)); 1.230 + nsCOMPtr<DataTransfer> dataTransfer = do_QueryInterface(domDataTransfer); 1.231 + NS_ENSURE_TRUE(dataTransfer, false); 1.232 + 1.233 + // We only support dropping files onto a file upload control 1.234 + nsRefPtr<DOMStringList> types = dataTransfer->Types(); 1.235 + return types->Contains(NS_LITERAL_STRING("Files")); 1.236 +} 1.237 + 1.238 +nscoord 1.239 +nsFileControlFrame::GetMinWidth(nsRenderingContext *aRenderingContext) 1.240 +{ 1.241 + nscoord result; 1.242 + DISPLAY_MIN_WIDTH(this, result); 1.243 + 1.244 + // Our min width is our pref width 1.245 + result = GetPrefWidth(aRenderingContext); 1.246 + return result; 1.247 +} 1.248 + 1.249 +void 1.250 +nsFileControlFrame::SyncDisabledState() 1.251 +{ 1.252 + EventStates eventStates = mContent->AsElement()->State(); 1.253 + if (eventStates.HasState(NS_EVENT_STATE_DISABLED)) { 1.254 + mBrowse->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, EmptyString(), 1.255 + true); 1.256 + } else { 1.257 + mBrowse->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, true); 1.258 + } 1.259 +} 1.260 + 1.261 +nsresult 1.262 +nsFileControlFrame::AttributeChanged(int32_t aNameSpaceID, 1.263 + nsIAtom* aAttribute, 1.264 + int32_t aModType) 1.265 +{ 1.266 + if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::tabindex) { 1.267 + if (aModType == nsIDOMMutationEvent::REMOVAL) { 1.268 + mBrowse->UnsetAttr(aNameSpaceID, aAttribute, true); 1.269 + } else { 1.270 + nsAutoString value; 1.271 + mContent->GetAttr(aNameSpaceID, aAttribute, value); 1.272 + mBrowse->SetAttr(aNameSpaceID, aAttribute, value, true); 1.273 + } 1.274 + } 1.275 + 1.276 + return nsBlockFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType); 1.277 +} 1.278 + 1.279 +void 1.280 +nsFileControlFrame::ContentStatesChanged(EventStates aStates) 1.281 +{ 1.282 + if (aStates.HasState(NS_EVENT_STATE_DISABLED)) { 1.283 + nsContentUtils::AddScriptRunner(new SyncDisabledStateEvent(this)); 1.284 + } 1.285 +} 1.286 + 1.287 +#ifdef DEBUG_FRAME_DUMP 1.288 +nsresult 1.289 +nsFileControlFrame::GetFrameName(nsAString& aResult) const 1.290 +{ 1.291 + return MakeFrameName(NS_LITERAL_STRING("FileControl"), aResult); 1.292 +} 1.293 +#endif 1.294 + 1.295 +void 1.296 +nsFileControlFrame::UpdateDisplayedValue(const nsAString& aValue, bool aNotify) 1.297 +{ 1.298 + mTextContent->SetAttr(kNameSpaceID_None, nsGkAtoms::value, aValue, aNotify); 1.299 +} 1.300 + 1.301 +nsresult 1.302 +nsFileControlFrame::SetFormProperty(nsIAtom* aName, 1.303 + const nsAString& aValue) 1.304 +{ 1.305 + if (nsGkAtoms::value == aName) { 1.306 + UpdateDisplayedValue(aValue, true); 1.307 + } 1.308 + return NS_OK; 1.309 +} 1.310 + 1.311 +void 1.312 +nsFileControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 1.313 + const nsRect& aDirtyRect, 1.314 + const nsDisplayListSet& aLists) 1.315 +{ 1.316 + BuildDisplayListForInline(aBuilder, aDirtyRect, aLists); 1.317 +} 1.318 + 1.319 +#ifdef ACCESSIBILITY 1.320 +a11y::AccType 1.321 +nsFileControlFrame::AccessibleType() 1.322 +{ 1.323 + return a11y::eHTMLFileInputType; 1.324 +} 1.325 +#endif 1.326 + 1.327 +//////////////////////////////////////////////////////////// 1.328 +// Mouse listener implementation 1.329 + 1.330 +NS_IMPL_ISUPPORTS(nsFileControlFrame::MouseListener, 1.331 + nsIDOMEventListener)