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: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 "nsWindowDataSource.h"
7 #include "nsIXULWindow.h"
8 #include "rdf.h"
9 #include "nsIRDFContainerUtils.h"
10 #include "nsIServiceManager.h"
11 #include "nsReadableUtils.h"
12 #include "nsIObserverService.h"
13 #include "nsIWindowMediator.h"
14 #include "nsXPCOMCID.h"
15 #include "mozilla/ModuleUtils.h"
16 #include "nsString.h"
18 // just to do the reverse-lookup! sheesh.
19 #include "nsIInterfaceRequestorUtils.h"
20 #include "nsIDocShell.h"
22 uint32_t nsWindowDataSource::windowCount = 0;
24 nsIRDFResource* nsWindowDataSource::kNC_Name = nullptr;
25 nsIRDFResource* nsWindowDataSource::kNC_WindowRoot = nullptr;
26 nsIRDFResource* nsWindowDataSource::kNC_KeyIndex = nullptr;
28 nsIRDFService* nsWindowDataSource::gRDFService = nullptr;
30 uint32_t nsWindowDataSource::gRefCnt = 0;
32 static const char kURINC_WindowRoot[] = "NC:WindowMediatorRoot";
34 DEFINE_RDF_VOCAB(NC_NAMESPACE_URI, NC, Name);
35 DEFINE_RDF_VOCAB(NC_NAMESPACE_URI, NC, KeyIndex);
37 nsresult
38 nsWindowDataSource::Init()
39 {
40 nsresult rv;
42 if (gRefCnt++ == 0) {
43 rv = CallGetService("@mozilla.org/rdf/rdf-service;1", &gRDFService);
44 if (NS_FAILED(rv)) return rv;
46 gRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_WindowRoot), &kNC_WindowRoot);
47 gRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_Name), &kNC_Name);
48 gRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_KeyIndex), &kNC_KeyIndex);
49 }
51 mInner = do_CreateInstance("@mozilla.org/rdf/datasource;1?name=in-memory-datasource", &rv);
52 if (NS_FAILED(rv)) return rv;
54 nsCOMPtr<nsIRDFContainerUtils> rdfc =
55 do_GetService("@mozilla.org/rdf/container-utils;1", &rv);
56 if (NS_FAILED(rv)) return rv;
58 rv = rdfc->MakeSeq(this, kNC_WindowRoot, getter_AddRefs(mContainer));
59 if (NS_FAILED(rv)) return rv;
61 nsCOMPtr<nsIWindowMediator> windowMediator =
62 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv);
63 if (NS_FAILED(rv)) return rv;
65 rv = windowMediator->AddListener(this);
66 if (NS_FAILED(rv)) return rv;
68 nsCOMPtr<nsIObserverService> observerService =
69 do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
70 if (NS_SUCCEEDED(rv)) {
71 rv = observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
72 false);
73 }
74 return NS_OK;
75 }
77 nsWindowDataSource::~nsWindowDataSource()
78 {
79 if (--gRefCnt == 0) {
80 NS_IF_RELEASE(kNC_Name);
81 NS_IF_RELEASE(kNC_KeyIndex);
82 NS_IF_RELEASE(kNC_WindowRoot);
83 NS_IF_RELEASE(gRDFService);
84 }
85 }
87 NS_IMETHODIMP
88 nsWindowDataSource::Observe(nsISupports *aSubject, const char* aTopic, const char16_t *aData)
89 {
90 if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
91 // release these objects so that they release their reference
92 // to us
93 mContainer = nullptr;
94 mInner = nullptr;
95 }
97 return NS_OK;
98 }
100 NS_IMPL_CYCLE_COLLECTION_CLASS(nsWindowDataSource)
102 NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsWindowDataSource)
103 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsWindowDataSource)
104 // XXX mContainer?
105 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInner)
106 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
108 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsWindowDataSource)
109 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsWindowDataSource)
111 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsWindowDataSource)
112 NS_INTERFACE_MAP_ENTRY(nsIObserver)
113 NS_INTERFACE_MAP_ENTRY(nsIWindowMediatorListener)
114 NS_INTERFACE_MAP_ENTRY(nsIWindowDataSource)
115 NS_INTERFACE_MAP_ENTRY(nsIRDFDataSource)
116 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
117 NS_INTERFACE_MAP_END
119 // nsIWindowMediatorListener implementation
120 // handle notifications from the window mediator and reflect them into
121 // RDF
123 /* void onWindowTitleChange (in nsIXULWindow window, in wstring newTitle); */
124 NS_IMETHODIMP
125 nsWindowDataSource::OnWindowTitleChange(nsIXULWindow *window,
126 const char16_t *newTitle)
127 {
128 nsresult rv;
130 nsCOMPtr<nsIRDFResource> windowResource;
131 mWindowResources.Get(window, getter_AddRefs(windowResource));
133 // oops, make sure this window is in the hashtable!
134 if (!windowResource) {
135 OnOpenWindow(window);
136 mWindowResources.Get(window, getter_AddRefs(windowResource));
137 }
139 NS_ENSURE_TRUE(windowResource, NS_ERROR_UNEXPECTED);
141 nsCOMPtr<nsIRDFLiteral> newTitleLiteral;
142 rv = gRDFService->GetLiteral(newTitle, getter_AddRefs(newTitleLiteral));
143 NS_ENSURE_SUCCESS(rv, rv);
145 // get the old title
146 nsCOMPtr<nsIRDFNode> oldTitleNode;
147 rv = GetTarget(windowResource, kNC_Name, true,
148 getter_AddRefs(oldTitleNode));
150 // assert the change
151 if (NS_SUCCEEDED(rv) && oldTitleNode)
152 // has an existing window title, update it
153 rv = Change(windowResource, kNC_Name, oldTitleNode, newTitleLiteral);
154 else
155 // removed from the tasklist
156 rv = Assert(windowResource, kNC_Name, newTitleLiteral, true);
158 if (rv != NS_RDF_ASSERTION_ACCEPTED)
159 {
160 NS_ERROR("unable to set window name");
161 }
163 return NS_OK;
164 }
166 /* void onOpenWindow (in nsIXULWindow window); */
167 NS_IMETHODIMP
168 nsWindowDataSource::OnOpenWindow(nsIXULWindow *window)
169 {
170 nsAutoCString windowId(NS_LITERAL_CSTRING("window-"));
171 windowId.AppendInt(windowCount++, 10);
173 nsCOMPtr<nsIRDFResource> windowResource;
174 gRDFService->GetResource(windowId, getter_AddRefs(windowResource));
176 mWindowResources.Put(window, windowResource);
178 // assert the new window
179 if (mContainer)
180 mContainer->AppendElement(windowResource);
182 return NS_OK;
183 }
185 /* void onCloseWindow (in nsIXULWindow window); */
186 NS_IMETHODIMP
187 nsWindowDataSource::OnCloseWindow(nsIXULWindow *window)
188 {
189 nsresult rv;
190 nsCOMPtr<nsIRDFResource> resource;
191 mWindowResources.Get(window, getter_AddRefs(resource));
192 if (!resource) {
193 return NS_ERROR_UNEXPECTED;
194 }
196 mWindowResources.Remove(window);
198 // make sure we're not shutting down
199 if (!mContainer) return NS_OK;
201 nsCOMPtr<nsIRDFNode> oldKeyNode;
202 nsCOMPtr<nsIRDFInt> oldKeyInt;
204 // get the old keyIndex, if any
205 rv = GetTarget(resource, kNC_KeyIndex, true,
206 getter_AddRefs(oldKeyNode));
207 if (NS_SUCCEEDED(rv) && (rv != NS_RDF_NO_VALUE))
208 oldKeyInt = do_QueryInterface(oldKeyNode);
211 // update RDF and keyindex - from this point forward we'll ignore
212 // errors, because they just indicate some kind of RDF inconsistency
213 int32_t winIndex = -1;
214 rv = mContainer->IndexOf(resource, &winIndex);
216 if (NS_FAILED(rv))
217 return NS_OK;
219 // unassert the old window, ignore any error
220 mContainer->RemoveElement(resource, true);
222 nsCOMPtr<nsISimpleEnumerator> children;
223 rv = mContainer->GetElements(getter_AddRefs(children));
224 if (NS_FAILED(rv))
225 return NS_OK;
227 bool more = false;
229 while (NS_SUCCEEDED(rv = children->HasMoreElements(&more)) && more) {
230 nsCOMPtr<nsISupports> sup;
231 rv = children->GetNext(getter_AddRefs(sup));
232 if (NS_FAILED(rv))
233 break;
235 nsCOMPtr<nsIRDFResource> windowResource = do_QueryInterface(sup, &rv);
236 if (NS_FAILED(rv))
237 continue;
239 int32_t currentIndex = -1;
240 mContainer->IndexOf(windowResource, ¤tIndex);
242 // can skip updating windows with lower indexes
243 // than the window that was removed
244 if (currentIndex < winIndex)
245 continue;
247 nsCOMPtr<nsIRDFNode> newKeyNode;
248 nsCOMPtr<nsIRDFInt> newKeyInt;
250 rv = GetTarget(windowResource, kNC_KeyIndex, true,
251 getter_AddRefs(newKeyNode));
252 if (NS_SUCCEEDED(rv) && (rv != NS_RDF_NO_VALUE))
253 newKeyInt = do_QueryInterface(newKeyNode);
255 // changing from one key index to another
256 if (oldKeyInt && newKeyInt)
257 Change(windowResource, kNC_KeyIndex, oldKeyInt, newKeyInt);
258 // creating a new keyindex - probably window going
259 // from (none) to "9"
260 else if (newKeyInt)
261 Assert(windowResource, kNC_KeyIndex, newKeyInt, true);
263 // somehow inserting a window above this one,
264 // "9" to (none)
265 else if (oldKeyInt)
266 Unassert(windowResource, kNC_KeyIndex, oldKeyInt);
268 }
269 return NS_OK;
270 }
272 struct findWindowClosure {
273 nsIRDFResource* targetResource;
274 nsIXULWindow *resultWindow;
275 };
277 static PLDHashOperator
278 findWindow(nsIXULWindow* aWindow, nsIRDFResource* aResource, void* aClosure)
279 {
280 findWindowClosure* closure = static_cast<findWindowClosure*>(aClosure);
282 if (aResource == closure->targetResource) {
283 closure->resultWindow = aWindow;
284 return PL_DHASH_STOP;
285 }
286 return PL_DHASH_NEXT;
287 }
289 // nsIWindowDataSource implementation
291 NS_IMETHODIMP
292 nsWindowDataSource::GetWindowForResource(const char *aResourceString,
293 nsIDOMWindow** aResult)
294 {
295 nsCOMPtr<nsIRDFResource> windowResource;
296 gRDFService->GetResource(nsDependentCString(aResourceString),
297 getter_AddRefs(windowResource));
299 // now reverse-lookup in the hashtable
300 findWindowClosure closure = { windowResource.get(), nullptr };
301 mWindowResources.EnumerateRead(findWindow, &closure);
302 if (closure.resultWindow) {
304 // this sucks, we have to jump through docshell to go from
305 // nsIXULWindow -> nsIDOMWindow
306 nsCOMPtr<nsIDocShell> docShell;
307 closure.resultWindow->GetDocShell(getter_AddRefs(docShell));
309 if (docShell) {
310 nsCOMPtr<nsIDOMWindow> result = do_GetInterface(docShell);
312 *aResult = result;
313 NS_IF_ADDREF(*aResult);
314 }
315 }
317 return NS_OK;
318 }
321 // nsIRDFDataSource implementation
322 // mostly, we just forward to mInner, except:
323 // GetURI() - need to return "rdf:window-mediator"
324 // GetTarget() - need to handle kNC_KeyIndex
327 /* readonly attribute string URI; */
328 NS_IMETHODIMP nsWindowDataSource::GetURI(char * *aURI)
329 {
330 NS_ENSURE_ARG_POINTER(aURI);
332 *aURI = ToNewCString(NS_LITERAL_CSTRING("rdf:window-mediator"));
334 if (!*aURI)
335 return NS_ERROR_OUT_OF_MEMORY;
337 return NS_OK;
338 }
340 /* nsIRDFNode GetTarget (in nsIRDFResource aSource, in nsIRDFResource aProperty, in boolean aTruthValue); */
341 NS_IMETHODIMP nsWindowDataSource::GetTarget(nsIRDFResource *aSource, nsIRDFResource *aProperty, bool aTruthValue, nsIRDFNode **_retval)
342 {
343 NS_ENSURE_ARG_POINTER(_retval);
345 // add extra nullptr checking for top-crash bug # 146466
346 if (!gRDFService) return NS_RDF_NO_VALUE;
347 if (!mInner) return NS_RDF_NO_VALUE;
348 if (!mContainer) return NS_RDF_NO_VALUE;
349 // special case kNC_KeyIndex before we forward to mInner
350 if (aProperty == kNC_KeyIndex) {
352 int32_t theIndex = 0;
353 nsresult rv = mContainer->IndexOf(aSource, &theIndex);
354 if (NS_FAILED(rv)) return rv;
356 // only allow the range of 1 to 9 for single key access
357 if (theIndex < 1 || theIndex > 9) return(NS_RDF_NO_VALUE);
359 nsCOMPtr<nsIRDFInt> indexInt;
360 rv = gRDFService->GetIntLiteral(theIndex, getter_AddRefs(indexInt));
361 if (NS_FAILED(rv)) return(rv);
362 if (!indexInt) return(NS_ERROR_FAILURE);
364 return CallQueryInterface(indexInt, _retval);
365 }
367 return mInner->GetTarget(aSource, aProperty, aTruthValue, _retval);
368 }
370 /* nsIRDFResource GetSource (in nsIRDFResource aProperty, in nsIRDFNode aTarget, in boolean aTruthValue); */
371 NS_IMETHODIMP nsWindowDataSource::GetSource(nsIRDFResource *aProperty, nsIRDFNode *aTarget, bool aTruthValue, nsIRDFResource **_retval)
372 {
373 if (mInner)
374 return mInner->GetSource(aProperty, aTarget, aTruthValue, _retval);
375 return NS_OK;
376 }
378 /* nsISimpleEnumerator GetSources (in nsIRDFResource aProperty, in nsIRDFNode aTarget, in boolean aTruthValue); */
379 NS_IMETHODIMP nsWindowDataSource::GetSources(nsIRDFResource *aProperty, nsIRDFNode *aTarget, bool aTruthValue, nsISimpleEnumerator **_retval)
380 {
381 if (mInner)
382 return mInner->GetSources(aProperty, aTarget, aTruthValue, _retval);
383 return NS_OK;
384 }
386 /* nsISimpleEnumerator GetTargets (in nsIRDFResource aSource, in nsIRDFResource aProperty, in boolean aTruthValue); */
387 NS_IMETHODIMP nsWindowDataSource::GetTargets(nsIRDFResource *aSource, nsIRDFResource *aProperty, bool aTruthValue, nsISimpleEnumerator **_retval)
388 {
389 if (mInner)
390 return mInner->GetTargets(aSource, aProperty, aTruthValue, _retval);
391 return NS_OK;
392 }
394 /* void Assert (in nsIRDFResource aSource, in nsIRDFResource aProperty, in nsIRDFNode aTarget, in boolean aTruthValue); */
395 NS_IMETHODIMP nsWindowDataSource::Assert(nsIRDFResource *aSource, nsIRDFResource *aProperty, nsIRDFNode *aTarget, bool aTruthValue)
396 {
397 if (mInner)
398 return mInner->Assert(aSource, aProperty, aTarget, aTruthValue);
399 return NS_OK;
400 }
402 /* void Unassert (in nsIRDFResource aSource, in nsIRDFResource aProperty, in nsIRDFNode aTarget); */
403 NS_IMETHODIMP nsWindowDataSource::Unassert(nsIRDFResource *aSource, nsIRDFResource *aProperty, nsIRDFNode *aTarget)
404 {
405 if (mInner)
406 return mInner->Unassert(aSource, aProperty, aTarget);
407 return NS_OK;
408 }
410 /* void Change (in nsIRDFResource aSource, in nsIRDFResource aProperty, in nsIRDFNode aOldTarget, in nsIRDFNode aNewTarget); */
411 NS_IMETHODIMP nsWindowDataSource::Change(nsIRDFResource *aSource, nsIRDFResource *aProperty, nsIRDFNode *aOldTarget, nsIRDFNode *aNewTarget)
412 {
413 if (mInner)
414 return mInner->Change(aSource, aProperty, aOldTarget, aNewTarget);
415 return NS_OK;
416 }
418 /* void Move (in nsIRDFResource aOldSource, in nsIRDFResource aNewSource, in nsIRDFResource aProperty, in nsIRDFNode aTarget); */
419 NS_IMETHODIMP nsWindowDataSource::Move(nsIRDFResource *aOldSource, nsIRDFResource *aNewSource, nsIRDFResource *aProperty, nsIRDFNode *aTarget)
420 {
421 if (mInner)
422 return mInner->Move(aOldSource, aNewSource, aProperty, aTarget);
423 return NS_OK;
424 }
426 /* boolean HasAssertion (in nsIRDFResource aSource, in nsIRDFResource aProperty, in nsIRDFNode aTarget, in boolean aTruthValue); */
427 NS_IMETHODIMP nsWindowDataSource::HasAssertion(nsIRDFResource *aSource, nsIRDFResource *aProperty, nsIRDFNode *aTarget, bool aTruthValue, bool *_retval)
428 {
429 if (mInner)
430 return mInner->HasAssertion(aSource, aProperty, aTarget, aTruthValue, _retval);
431 return NS_OK;
432 }
434 /* void AddObserver (in nsIRDFObserver aObserver); */
435 NS_IMETHODIMP nsWindowDataSource::AddObserver(nsIRDFObserver *aObserver)
436 {
437 if (mInner)
438 return mInner->AddObserver(aObserver);
439 return NS_OK;
440 }
442 /* void RemoveObserver (in nsIRDFObserver aObserver); */
443 NS_IMETHODIMP nsWindowDataSource::RemoveObserver(nsIRDFObserver *aObserver)
444 {
445 if (mInner)
446 return mInner->RemoveObserver(aObserver);
447 return NS_OK;
448 }
450 /* nsISimpleEnumerator ArcLabelsIn (in nsIRDFNode aNode); */
451 NS_IMETHODIMP nsWindowDataSource::ArcLabelsIn(nsIRDFNode *aNode, nsISimpleEnumerator **_retval)
452 {
453 if (mInner)
454 return mInner->ArcLabelsIn(aNode, _retval);
455 return NS_OK;
456 }
458 /* nsISimpleEnumerator ArcLabelsOut (in nsIRDFResource aSource); */
459 NS_IMETHODIMP nsWindowDataSource::ArcLabelsOut(nsIRDFResource *aSource, nsISimpleEnumerator **_retval)
460 {
461 if (mInner)
462 return mInner->ArcLabelsOut(aSource, _retval);
463 return NS_OK;
464 }
466 /* nsISimpleEnumerator GetAllResources (); */
467 NS_IMETHODIMP nsWindowDataSource::GetAllResources(nsISimpleEnumerator **_retval)
468 {
469 if (mInner)
470 return mInner->GetAllResources(_retval);
471 return NS_OK;
472 }
474 /* boolean IsCommandEnabled (in nsISupportsArray aSources, in nsIRDFResource aCommand, in nsISupportsArray aArguments); */
475 NS_IMETHODIMP nsWindowDataSource::IsCommandEnabled(nsISupportsArray *aSources, nsIRDFResource *aCommand, nsISupportsArray *aArguments, bool *_retval)
476 {
477 if (mInner)
478 return mInner->IsCommandEnabled(aSources, aCommand, aArguments, _retval);
479 return NS_OK;
480 }
482 /* void DoCommand (in nsISupportsArray aSources, in nsIRDFResource aCommand, in nsISupportsArray aArguments); */
483 NS_IMETHODIMP nsWindowDataSource::DoCommand(nsISupportsArray *aSources, nsIRDFResource *aCommand, nsISupportsArray *aArguments)
484 {
485 if (mInner)
486 return mInner->DoCommand(aSources, aCommand, aArguments);
487 return NS_OK;
488 }
490 /* nsISimpleEnumerator GetAllCmds (in nsIRDFResource aSource); */
491 NS_IMETHODIMP nsWindowDataSource::GetAllCmds(nsIRDFResource *aSource, nsISimpleEnumerator **_retval)
492 {
493 if (mInner)
494 return mInner->GetAllCmds(aSource, _retval);
495 return NS_OK;
496 }
498 /* boolean hasArcIn (in nsIRDFNode aNode, in nsIRDFResource aArc); */
499 NS_IMETHODIMP nsWindowDataSource::HasArcIn(nsIRDFNode *aNode, nsIRDFResource *aArc, bool *_retval)
500 {
501 if (mInner)
502 return mInner->HasArcIn(aNode, aArc, _retval);
503 return NS_OK;
504 }
506 /* boolean hasArcOut (in nsIRDFResource aSource, in nsIRDFResource aArc); */
507 NS_IMETHODIMP nsWindowDataSource::HasArcOut(nsIRDFResource *aSource, nsIRDFResource *aArc, bool *_retval)
508 {
509 if (mInner)
510 return mInner->HasArcOut(aSource, aArc, _retval);
511 return NS_OK;
512 }
514 /* void beginUpdateBatch (); */
515 NS_IMETHODIMP nsWindowDataSource::BeginUpdateBatch()
516 {
517 if (mInner)
518 return mInner->BeginUpdateBatch();
519 return NS_OK;
520 }
522 /* void endUpdateBatch (); */
523 NS_IMETHODIMP nsWindowDataSource::EndUpdateBatch()
524 {
525 if (mInner)
526 return mInner->EndUpdateBatch();
527 return NS_OK;
528 }
530 // The module goop
532 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsWindowDataSource, Init)
534 NS_DEFINE_NAMED_CID(NS_WINDOWDATASOURCE_CID);
536 static const mozilla::Module::CIDEntry kWindowDSCIDs[] = {
537 { &kNS_WINDOWDATASOURCE_CID, false, nullptr, nsWindowDataSourceConstructor },
538 { nullptr }
539 };
541 static const mozilla::Module::ContractIDEntry kWindowDSContracts[] = {
542 { NS_RDF_DATASOURCE_CONTRACTID_PREFIX "window-mediator", &kNS_WINDOWDATASOURCE_CID },
543 { nullptr }
544 };
546 static const mozilla::Module::CategoryEntry kWindowDSCategories[] = {
547 { "app-startup", "Window Data Source", "service," NS_RDF_DATASOURCE_CONTRACTID_PREFIX "window-mediator" },
548 { nullptr }
549 };
551 static const mozilla::Module kWindowDSModule = {
552 mozilla::Module::kVersion,
553 kWindowDSCIDs,
554 kWindowDSContracts,
555 kWindowDSCategories
556 };
558 NSMODULE_DEFN(nsWindowDataSourceModule) = &kWindowDSModule;