layout/forms/nsFileControlFrame.cpp

changeset 0
6474c204b198
     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)

mercurial