Bumping manifests a=b2g-bump
[gecko.git] / dom / base / nsContentPermissionHelper.cpp
blob4d955b6dadf8eb9945403ed0a579ce7977de9c5b
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #ifdef MOZ_WIDGET_GONK
6 #include "GonkPermission.h"
7 #include "mozilla/dom/ContentParent.h"
8 #endif // MOZ_WIDGET_GONK
9 #include "nsCOMPtr.h"
10 #include "nsIDOMElement.h"
11 #include "nsIPrincipal.h"
12 #include "mozilla/dom/Element.h"
13 #include "mozilla/dom/PContentPermission.h"
14 #include "mozilla/dom/PermissionMessageUtils.h"
15 #include "mozilla/dom/PContentPermissionRequestParent.h"
16 #include "mozilla/dom/ScriptSettings.h"
17 #include "mozilla/dom/TabChild.h"
18 #include "mozilla/dom/TabParent.h"
19 #include "mozilla/unused.h"
20 #include "nsComponentManagerUtils.h"
21 #include "nsArrayUtils.h"
22 #include "nsIMutableArray.h"
23 #include "nsContentPermissionHelper.h"
24 #include "nsJSUtils.h"
25 #include "nsISupportsPrimitives.h"
26 #include "nsServiceManagerUtils.h"
28 using mozilla::unused; // <snicker>
29 using namespace mozilla::dom;
30 using namespace mozilla;
32 namespace mozilla {
33 namespace dom {
35 class ContentPermissionRequestParent : public PContentPermissionRequestParent
37 public:
38 ContentPermissionRequestParent(const nsTArray<PermissionRequest>& aRequests,
39 Element* element,
40 const IPC::Principal& principal);
41 virtual ~ContentPermissionRequestParent();
43 bool IsBeingDestroyed();
45 nsCOMPtr<nsIPrincipal> mPrincipal;
46 nsCOMPtr<Element> mElement;
47 nsRefPtr<nsContentPermissionRequestProxy> mProxy;
48 nsTArray<PermissionRequest> mRequests;
50 private:
51 virtual bool Recvprompt();
52 virtual void ActorDestroy(ActorDestroyReason why);
55 ContentPermissionRequestParent::ContentPermissionRequestParent(const nsTArray<PermissionRequest>& aRequests,
56 Element* aElement,
57 const IPC::Principal& aPrincipal)
59 MOZ_COUNT_CTOR(ContentPermissionRequestParent);
61 mPrincipal = aPrincipal;
62 mElement = aElement;
63 mRequests = aRequests;
66 ContentPermissionRequestParent::~ContentPermissionRequestParent()
68 MOZ_COUNT_DTOR(ContentPermissionRequestParent);
71 bool
72 ContentPermissionRequestParent::Recvprompt()
74 mProxy = new nsContentPermissionRequestProxy();
75 NS_ASSERTION(mProxy, "Alloc of request proxy failed");
76 if (NS_FAILED(mProxy->Init(mRequests, this))) {
77 mProxy->Cancel();
79 return true;
82 void
83 ContentPermissionRequestParent::ActorDestroy(ActorDestroyReason why)
85 if (mProxy) {
86 mProxy->OnParentDestroyed();
90 bool
91 ContentPermissionRequestParent::IsBeingDestroyed()
93 // When TabParent::Destroy() is called, we are being destroyed. It's unsafe
94 // to send out any message now.
95 TabParent* tabParent = static_cast<TabParent*>(Manager());
96 return tabParent->IsDestroyed();
99 NS_IMPL_ISUPPORTS(ContentPermissionType, nsIContentPermissionType)
101 ContentPermissionType::ContentPermissionType(const nsACString& aType,
102 const nsACString& aAccess,
103 const nsTArray<nsString>& aOptions)
105 mType = aType;
106 mAccess = aAccess;
107 mOptions = aOptions;
110 ContentPermissionType::~ContentPermissionType()
114 NS_IMETHODIMP
115 ContentPermissionType::GetType(nsACString& aType)
117 aType = mType;
118 return NS_OK;
121 NS_IMETHODIMP
122 ContentPermissionType::GetAccess(nsACString& aAccess)
124 aAccess = mAccess;
125 return NS_OK;
128 NS_IMETHODIMP
129 ContentPermissionType::GetOptions(nsIArray** aOptions)
131 NS_ENSURE_ARG_POINTER(aOptions);
133 *aOptions = nullptr;
135 nsresult rv;
136 nsCOMPtr<nsIMutableArray> options =
137 do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
138 NS_ENSURE_SUCCESS(rv, rv);
140 // copy options into JS array
141 for (uint32_t i = 0; i < mOptions.Length(); ++i) {
142 nsCOMPtr<nsISupportsString> isupportsString =
143 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
144 NS_ENSURE_SUCCESS(rv, rv);
146 rv = isupportsString->SetData(mOptions[i]);
147 NS_ENSURE_SUCCESS(rv, rv);
149 rv = options->AppendElement(isupportsString, false);
150 NS_ENSURE_SUCCESS(rv, rv);
153 NS_ADDREF(*aOptions = options);
154 return NS_OK;
157 // nsContentPermissionUtils
159 /* static */ uint32_t
160 nsContentPermissionUtils::ConvertPermissionRequestToArray(nsTArray<PermissionRequest>& aSrcArray,
161 nsIMutableArray* aDesArray)
163 uint32_t len = aSrcArray.Length();
164 for (uint32_t i = 0; i < len; i++) {
165 nsRefPtr<ContentPermissionType> cpt =
166 new ContentPermissionType(aSrcArray[i].type(),
167 aSrcArray[i].access(),
168 aSrcArray[i].options());
169 aDesArray->AppendElement(cpt, false);
171 return len;
174 /* static */ uint32_t
175 nsContentPermissionUtils::ConvertArrayToPermissionRequest(nsIArray* aSrcArray,
176 nsTArray<PermissionRequest>& aDesArray)
178 uint32_t len = 0;
179 aSrcArray->GetLength(&len);
180 for (uint32_t i = 0; i < len; i++) {
181 nsCOMPtr<nsIContentPermissionType> cpt = do_QueryElementAt(aSrcArray, i);
182 nsAutoCString type;
183 nsAutoCString access;
184 cpt->GetType(type);
185 cpt->GetAccess(access);
187 nsCOMPtr<nsIArray> optionArray;
188 cpt->GetOptions(getter_AddRefs(optionArray));
189 uint32_t optionsLength = 0;
190 if (optionArray) {
191 optionArray->GetLength(&optionsLength);
193 nsTArray<nsString> options;
194 for (uint32_t j = 0; j < optionsLength; ++j) {
195 nsCOMPtr<nsISupportsString> isupportsString = do_QueryElementAt(optionArray, j);
196 if (isupportsString) {
197 nsString option;
198 isupportsString->GetData(option);
199 options.AppendElement(option);
203 aDesArray.AppendElement(PermissionRequest(type, access, options));
205 return len;
209 /* static */ nsresult
210 nsContentPermissionUtils::CreatePermissionArray(const nsACString& aType,
211 const nsACString& aAccess,
212 const nsTArray<nsString>& aOptions,
213 nsIArray** aTypesArray)
215 nsCOMPtr<nsIMutableArray> types = do_CreateInstance(NS_ARRAY_CONTRACTID);
216 nsRefPtr<ContentPermissionType> permType = new ContentPermissionType(aType,
217 aAccess,
218 aOptions);
219 types->AppendElement(permType, false);
220 types.forget(aTypesArray);
222 return NS_OK;
225 /* static */ PContentPermissionRequestParent*
226 nsContentPermissionUtils::CreateContentPermissionRequestParent(const nsTArray<PermissionRequest>& aRequests,
227 Element* element,
228 const IPC::Principal& principal)
230 return new ContentPermissionRequestParent(aRequests, element, principal);
233 /* static */ nsresult
234 nsContentPermissionUtils::AskPermission(nsIContentPermissionRequest* aRequest, nsPIDOMWindow* aWindow)
236 MOZ_ASSERT(!aWindow || aWindow->IsInnerWindow());
237 NS_ENSURE_STATE(aWindow && aWindow->IsCurrentInnerWindow());
239 // for content process
240 if (XRE_GetProcessType() == GeckoProcessType_Content) {
242 nsRefPtr<RemotePermissionRequest> req =
243 new RemotePermissionRequest(aRequest, aWindow);
245 MOZ_ASSERT(NS_IsMainThread()); // IPC can only be execute on main thread.
247 TabChild* child = TabChild::GetFrom(aWindow->GetDocShell());
248 NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
250 nsCOMPtr<nsIArray> typeArray;
251 nsresult rv = aRequest->GetTypes(getter_AddRefs(typeArray));
252 NS_ENSURE_SUCCESS(rv, rv);
254 nsTArray<PermissionRequest> permArray;
255 ConvertArrayToPermissionRequest(typeArray, permArray);
257 nsCOMPtr<nsIPrincipal> principal;
258 rv = aRequest->GetPrincipal(getter_AddRefs(principal));
259 NS_ENSURE_SUCCESS(rv, rv);
261 req->IPDLAddRef();
262 child->SendPContentPermissionRequestConstructor(req,
263 permArray,
264 IPC::Principal(principal));
266 req->Sendprompt();
267 return NS_OK;
270 // for chrome process
271 nsCOMPtr<nsIContentPermissionPrompt> prompt =
272 do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
273 if (prompt) {
274 prompt->Prompt(aRequest);
276 return NS_OK;
279 } // namespace dom
280 } // namespace mozilla
282 nsContentPermissionRequestProxy::nsContentPermissionRequestProxy()
284 MOZ_COUNT_CTOR(nsContentPermissionRequestProxy);
287 nsContentPermissionRequestProxy::~nsContentPermissionRequestProxy()
289 MOZ_COUNT_DTOR(nsContentPermissionRequestProxy);
292 nsresult
293 nsContentPermissionRequestProxy::Init(const nsTArray<PermissionRequest>& requests,
294 ContentPermissionRequestParent* parent)
296 NS_ASSERTION(parent, "null parent");
297 mParent = parent;
298 mPermissionRequests = requests;
300 nsCOMPtr<nsIContentPermissionPrompt> prompt = do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
301 if (!prompt) {
302 return NS_ERROR_FAILURE;
305 prompt->Prompt(this);
306 return NS_OK;
309 void
310 nsContentPermissionRequestProxy::OnParentDestroyed()
312 mParent = nullptr;
315 NS_IMPL_ISUPPORTS(nsContentPermissionRequestProxy, nsIContentPermissionRequest)
317 NS_IMETHODIMP
318 nsContentPermissionRequestProxy::GetTypes(nsIArray** aTypes)
320 nsCOMPtr<nsIMutableArray> types = do_CreateInstance(NS_ARRAY_CONTRACTID);
321 if (mozilla::dom::nsContentPermissionUtils::ConvertPermissionRequestToArray(mPermissionRequests, types)) {
322 types.forget(aTypes);
323 return NS_OK;
325 return NS_ERROR_FAILURE;
328 NS_IMETHODIMP
329 nsContentPermissionRequestProxy::GetWindow(nsIDOMWindow * *aRequestingWindow)
331 NS_ENSURE_ARG_POINTER(aRequestingWindow);
332 *aRequestingWindow = nullptr; // ipc doesn't have a window
333 return NS_OK;
336 NS_IMETHODIMP
337 nsContentPermissionRequestProxy::GetPrincipal(nsIPrincipal * *aRequestingPrincipal)
339 NS_ENSURE_ARG_POINTER(aRequestingPrincipal);
340 if (mParent == nullptr) {
341 return NS_ERROR_FAILURE;
344 NS_ADDREF(*aRequestingPrincipal = mParent->mPrincipal);
345 return NS_OK;
348 NS_IMETHODIMP
349 nsContentPermissionRequestProxy::GetElement(nsIDOMElement * *aRequestingElement)
351 NS_ENSURE_ARG_POINTER(aRequestingElement);
352 if (mParent == nullptr) {
353 return NS_ERROR_FAILURE;
356 nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(mParent->mElement);
357 elem.forget(aRequestingElement);
358 return NS_OK;
361 NS_IMETHODIMP
362 nsContentPermissionRequestProxy::Cancel()
364 if (mParent == nullptr) {
365 return NS_ERROR_FAILURE;
368 // Don't send out the delete message when the managing protocol (PBrowser) is
369 // being destroyed and PContentPermissionRequest will soon be.
370 if (mParent->IsBeingDestroyed()) {
371 return NS_ERROR_FAILURE;
374 nsTArray<PermissionChoice> emptyChoices;
376 unused << ContentPermissionRequestParent::Send__delete__(mParent, false, emptyChoices);
377 mParent = nullptr;
378 return NS_OK;
381 NS_IMETHODIMP
382 nsContentPermissionRequestProxy::Allow(JS::HandleValue aChoices)
384 if (mParent == nullptr) {
385 return NS_ERROR_FAILURE;
388 // Don't send out the delete message when the managing protocol (PBrowser) is
389 // being destroyed and PContentPermissionRequest will soon be.
390 if (mParent->IsBeingDestroyed()) {
391 return NS_ERROR_FAILURE;
394 #ifdef MOZ_WIDGET_GONK
395 uint32_t len = mPermissionRequests.Length();
396 for (uint32_t i = 0; i < len; i++) {
397 if (mPermissionRequests[i].type().EqualsLiteral("audio-capture")) {
398 GonkPermissionService::GetInstance()->addGrantInfo(
399 "android.permission.RECORD_AUDIO",
400 static_cast<TabParent*>(
401 mParent->Manager())->Manager()->AsContentParent()->Pid());
403 if (mPermissionRequests[i].type().EqualsLiteral("video-capture")) {
404 GonkPermissionService::GetInstance()->addGrantInfo(
405 "android.permission.CAMERA",
406 static_cast<TabParent*>(
407 mParent->Manager())->Manager()->AsContentParent()->Pid());
410 #endif
412 nsTArray<PermissionChoice> choices;
413 if (aChoices.isNullOrUndefined()) {
414 // No choice is specified.
415 } else if (aChoices.isObject()) {
416 // Iterate through all permission types.
417 for (uint32_t i = 0; i < mPermissionRequests.Length(); ++i) {
418 nsCString type = mPermissionRequests[i].type();
420 mozilla::AutoSafeJSContext cx;
421 JS::Rooted<JSObject*> obj(cx, &aChoices.toObject());
422 JSAutoCompartment ac(cx, obj);
424 JS::Rooted<JS::Value> val(cx);
426 if (!JS_GetProperty(cx, obj, type.BeginReading(), &val) ||
427 !val.isString()) {
428 // no setting for the permission type, skip it
429 } else {
430 nsAutoJSString choice;
431 if (!choice.init(cx, val)) {
432 return NS_ERROR_FAILURE;
434 choices.AppendElement(PermissionChoice(type, choice));
437 } else {
438 MOZ_ASSERT(false, "SelectedChoices should be undefined or an JS object");
439 return NS_ERROR_FAILURE;
442 unused << ContentPermissionRequestParent::Send__delete__(mParent, true, choices);
443 mParent = nullptr;
444 return NS_OK;
447 // RemotePermissionRequest
449 NS_IMPL_ISUPPORTS0(RemotePermissionRequest)
451 RemotePermissionRequest::RemotePermissionRequest(
452 nsIContentPermissionRequest* aRequest,
453 nsPIDOMWindow* aWindow)
454 : mRequest(aRequest)
455 , mWindow(aWindow)
456 , mIPCOpen(false)
460 void
461 RemotePermissionRequest::DoCancel()
463 NS_ASSERTION(mRequest, "We need a request");
464 mRequest->Cancel();
467 void
468 RemotePermissionRequest::DoAllow(JS::HandleValue aChoices)
470 NS_ASSERTION(mRequest, "We need a request");
471 mRequest->Allow(aChoices);
474 // PContentPermissionRequestChild
475 bool
476 RemotePermissionRequest::Recv__delete__(const bool& aAllow,
477 const nsTArray<PermissionChoice>& aChoices)
479 if (aAllow && mWindow->IsCurrentInnerWindow()) {
480 // Use 'undefined' if no choice is provided.
481 if (aChoices.IsEmpty()) {
482 DoAllow(JS::UndefinedHandleValue);
483 return true;
486 // Convert choices to a JS val if any.
487 // {"type1": "choice1", "type2": "choiceA"}
488 AutoJSAPI jsapi;
489 if (NS_WARN_IF(!jsapi.Init(mWindow))) {
490 return true; // This is not an IPC error.
493 JSContext* cx = jsapi.cx();
494 JS::Rooted<JSObject*> obj(cx);
495 obj = JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr());
496 for (uint32_t i = 0; i < aChoices.Length(); ++i) {
497 const nsString& choice = aChoices[i].choice();
498 const nsCString& type = aChoices[i].type();
499 JS::Rooted<JSString*> jChoice(cx, JS_NewUCStringCopyN(cx, choice.get(), choice.Length()));
500 JS::Rooted<JS::Value> vChoice(cx, StringValue(jChoice));
501 if (!JS_SetProperty(cx, obj, type.get(), vChoice)) {
502 return false;
505 JS::RootedValue val(cx, JS::ObjectValue(*obj));
506 DoAllow(val);
507 } else {
508 DoCancel();
510 return true;