Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsFileControlFrame.h"
8 #include "nsGkAtoms.h"
9 #include "nsCOMPtr.h"
10 #include "nsIDocument.h"
11 #include "nsINodeInfo.h"
12 #include "mozilla/dom/Element.h"
13 #include "mozilla/dom/DataTransfer.h"
14 #include "mozilla/dom/HTMLButtonElement.h"
15 #include "mozilla/dom/HTMLInputElement.h"
16 #include "nsNodeInfoManager.h"
17 #include "nsContentCreatorFunctions.h"
18 #include "nsContentUtils.h"
19 #include "mozilla/EventStates.h"
20 #include "mozilla/dom/DOMStringList.h"
21 #include "nsIDOMDragEvent.h"
22 #include "nsIDOMFileList.h"
23 #include "nsContentList.h"
24 #include "nsIDOMMutationEvent.h"
25 #include "nsTextNode.h"
27 using namespace mozilla;
28 using namespace mozilla::dom;
30 nsIFrame*
31 NS_NewFileControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
32 {
33 return new (aPresShell) nsFileControlFrame(aContext);
34 }
36 NS_IMPL_FRAMEARENA_HELPERS(nsFileControlFrame)
38 nsFileControlFrame::nsFileControlFrame(nsStyleContext* aContext)
39 : nsBlockFrame(aContext)
40 {
41 AddStateBits(NS_BLOCK_FLOAT_MGR);
42 }
45 void
46 nsFileControlFrame::Init(nsIContent* aContent,
47 nsIFrame* aParent,
48 nsIFrame* aPrevInFlow)
49 {
50 nsBlockFrame::Init(aContent, aParent, aPrevInFlow);
52 mMouseListener = new DnDListener(this);
53 }
55 void
56 nsFileControlFrame::DestroyFrom(nsIFrame* aDestructRoot)
57 {
58 ENSURE_TRUE(mContent);
60 // Remove the events.
61 if (mContent) {
62 mContent->RemoveSystemEventListener(NS_LITERAL_STRING("drop"),
63 mMouseListener, false);
64 mContent->RemoveSystemEventListener(NS_LITERAL_STRING("dragover"),
65 mMouseListener, false);
66 }
68 nsContentUtils::DestroyAnonymousContent(&mTextContent);
69 nsContentUtils::DestroyAnonymousContent(&mBrowse);
71 mMouseListener->ForgetFrame();
72 nsBlockFrame::DestroyFrom(aDestructRoot);
73 }
75 nsresult
76 nsFileControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
77 {
78 nsCOMPtr<nsIDocument> doc = mContent->GetDocument();
80 // Create and setup the file picking button.
81 mBrowse = doc->CreateHTMLElement(nsGkAtoms::button);
82 // NOTE: SetIsNativeAnonymousRoot() has to be called before setting any
83 // attribute.
84 mBrowse->SetIsNativeAnonymousRoot();
85 mBrowse->SetAttr(kNameSpaceID_None, nsGkAtoms::type,
86 NS_LITERAL_STRING("button"), false);
88 // Set the file picking button text depending on the current locale.
89 nsXPIDLString buttonTxt;
90 nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
91 "Browse", buttonTxt);
93 // Set the browse button text. It's a bit of a pain to do because we want to
94 // make sure we are not notifying.
95 nsRefPtr<nsTextNode> textContent =
96 new nsTextNode(mBrowse->NodeInfo()->NodeInfoManager());
98 textContent->SetText(buttonTxt, false);
100 nsresult rv = mBrowse->AppendChildTo(textContent, false);
101 NS_ENSURE_SUCCESS(rv, rv);
103 // Make sure access key and tab order for the element actually redirect to the
104 // file picking button.
105 nsRefPtr<HTMLInputElement> fileContent = HTMLInputElement::FromContentOrNull(mContent);
106 nsRefPtr<HTMLButtonElement> browseControl = HTMLButtonElement::FromContentOrNull(mBrowse);
108 nsAutoString accessKey;
109 fileContent->GetAccessKey(accessKey);
110 browseControl->SetAccessKey(accessKey);
112 int32_t tabIndex;
113 fileContent->GetTabIndex(&tabIndex);
114 browseControl->SetTabIndex(tabIndex);
116 if (!aElements.AppendElement(mBrowse)) {
117 return NS_ERROR_OUT_OF_MEMORY;
118 }
120 // Create and setup the text showing the selected files.
121 nsCOMPtr<nsINodeInfo> nodeInfo;
122 nodeInfo = doc->NodeInfoManager()->GetNodeInfo(nsGkAtoms::label, nullptr,
123 kNameSpaceID_XUL,
124 nsIDOMNode::ELEMENT_NODE);
125 NS_TrustedNewXULElement(getter_AddRefs(mTextContent), nodeInfo.forget());
126 // NOTE: SetIsNativeAnonymousRoot() has to be called before setting any
127 // attribute.
128 mTextContent->SetIsNativeAnonymousRoot();
129 mTextContent->SetAttr(kNameSpaceID_None, nsGkAtoms::crop,
130 NS_LITERAL_STRING("center"), false);
132 // Update the displayed text to reflect the current element's value.
133 nsAutoString value;
134 HTMLInputElement::FromContent(mContent)->GetDisplayFileName(value);
135 UpdateDisplayedValue(value, false);
137 if (!aElements.AppendElement(mTextContent)) {
138 return NS_ERROR_OUT_OF_MEMORY;
139 }
141 // We should be able to interact with the element by doing drag and drop.
142 mContent->AddSystemEventListener(NS_LITERAL_STRING("drop"),
143 mMouseListener, false);
144 mContent->AddSystemEventListener(NS_LITERAL_STRING("dragover"),
145 mMouseListener, false);
147 SyncDisabledState();
149 return NS_OK;
150 }
152 void
153 nsFileControlFrame::AppendAnonymousContentTo(nsBaseContentList& aElements,
154 uint32_t aFilter)
155 {
156 aElements.MaybeAppendElement(mBrowse);
157 aElements.MaybeAppendElement(mTextContent);
158 }
160 NS_QUERYFRAME_HEAD(nsFileControlFrame)
161 NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
162 NS_QUERYFRAME_ENTRY(nsIFormControlFrame)
163 NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
165 void
166 nsFileControlFrame::SetFocus(bool aOn, bool aRepaint)
167 {
168 }
170 /**
171 * This is called when we receive a drop or a dragover.
172 */
173 NS_IMETHODIMP
174 nsFileControlFrame::DnDListener::HandleEvent(nsIDOMEvent* aEvent)
175 {
176 NS_ASSERTION(mFrame, "We should have been unregistered");
178 bool defaultPrevented = false;
179 aEvent->GetDefaultPrevented(&defaultPrevented);
180 if (defaultPrevented) {
181 return NS_OK;
182 }
184 nsCOMPtr<nsIDOMDragEvent> dragEvent = do_QueryInterface(aEvent);
185 if (!dragEvent || !IsValidDropData(dragEvent)) {
186 return NS_OK;
187 }
189 nsAutoString eventType;
190 aEvent->GetType(eventType);
191 if (eventType.EqualsLiteral("dragover")) {
192 // Prevent default if we can accept this drag data
193 aEvent->PreventDefault();
194 return NS_OK;
195 }
197 if (eventType.EqualsLiteral("drop")) {
198 aEvent->StopPropagation();
199 aEvent->PreventDefault();
201 nsIContent* content = mFrame->GetContent();
202 NS_ASSERTION(content, "The frame has no content???");
204 HTMLInputElement* inputElement = HTMLInputElement::FromContent(content);
205 NS_ASSERTION(inputElement, "No input element for this file upload control frame!");
207 nsCOMPtr<nsIDOMDataTransfer> dataTransfer;
208 dragEvent->GetDataTransfer(getter_AddRefs(dataTransfer));
210 nsCOMPtr<nsIDOMFileList> fileList;
211 dataTransfer->GetFiles(getter_AddRefs(fileList));
213 inputElement->SetFiles(fileList, true);
214 nsContentUtils::DispatchTrustedEvent(content->OwnerDoc(), content,
215 NS_LITERAL_STRING("change"), true,
216 false);
217 }
219 return NS_OK;
220 }
222 /* static */ bool
223 nsFileControlFrame::DnDListener::IsValidDropData(nsIDOMDragEvent* aEvent)
224 {
225 nsCOMPtr<nsIDOMDataTransfer> domDataTransfer;
226 aEvent->GetDataTransfer(getter_AddRefs(domDataTransfer));
227 nsCOMPtr<DataTransfer> dataTransfer = do_QueryInterface(domDataTransfer);
228 NS_ENSURE_TRUE(dataTransfer, false);
230 // We only support dropping files onto a file upload control
231 nsRefPtr<DOMStringList> types = dataTransfer->Types();
232 return types->Contains(NS_LITERAL_STRING("Files"));
233 }
235 nscoord
236 nsFileControlFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
237 {
238 nscoord result;
239 DISPLAY_MIN_WIDTH(this, result);
241 // Our min width is our pref width
242 result = GetPrefWidth(aRenderingContext);
243 return result;
244 }
246 void
247 nsFileControlFrame::SyncDisabledState()
248 {
249 EventStates eventStates = mContent->AsElement()->State();
250 if (eventStates.HasState(NS_EVENT_STATE_DISABLED)) {
251 mBrowse->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, EmptyString(),
252 true);
253 } else {
254 mBrowse->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, true);
255 }
256 }
258 nsresult
259 nsFileControlFrame::AttributeChanged(int32_t aNameSpaceID,
260 nsIAtom* aAttribute,
261 int32_t aModType)
262 {
263 if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::tabindex) {
264 if (aModType == nsIDOMMutationEvent::REMOVAL) {
265 mBrowse->UnsetAttr(aNameSpaceID, aAttribute, true);
266 } else {
267 nsAutoString value;
268 mContent->GetAttr(aNameSpaceID, aAttribute, value);
269 mBrowse->SetAttr(aNameSpaceID, aAttribute, value, true);
270 }
271 }
273 return nsBlockFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
274 }
276 void
277 nsFileControlFrame::ContentStatesChanged(EventStates aStates)
278 {
279 if (aStates.HasState(NS_EVENT_STATE_DISABLED)) {
280 nsContentUtils::AddScriptRunner(new SyncDisabledStateEvent(this));
281 }
282 }
284 #ifdef DEBUG_FRAME_DUMP
285 nsresult
286 nsFileControlFrame::GetFrameName(nsAString& aResult) const
287 {
288 return MakeFrameName(NS_LITERAL_STRING("FileControl"), aResult);
289 }
290 #endif
292 void
293 nsFileControlFrame::UpdateDisplayedValue(const nsAString& aValue, bool aNotify)
294 {
295 mTextContent->SetAttr(kNameSpaceID_None, nsGkAtoms::value, aValue, aNotify);
296 }
298 nsresult
299 nsFileControlFrame::SetFormProperty(nsIAtom* aName,
300 const nsAString& aValue)
301 {
302 if (nsGkAtoms::value == aName) {
303 UpdateDisplayedValue(aValue, true);
304 }
305 return NS_OK;
306 }
308 void
309 nsFileControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
310 const nsRect& aDirtyRect,
311 const nsDisplayListSet& aLists)
312 {
313 BuildDisplayListForInline(aBuilder, aDirtyRect, aLists);
314 }
316 #ifdef ACCESSIBILITY
317 a11y::AccType
318 nsFileControlFrame::AccessibleType()
319 {
320 return a11y::eHTMLFileInputType;
321 }
322 #endif
324 ////////////////////////////////////////////////////////////
325 // Mouse listener implementation
327 NS_IMPL_ISUPPORTS(nsFileControlFrame::MouseListener,
328 nsIDOMEventListener)