nsContentPermissionHelper.cpp (27165B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "nsContentPermissionHelper.h" 8 9 #include <map> 10 11 #include "js/PropertyAndElement.h" // JS_GetProperty, JS_SetProperty 12 #include "mozilla/Attributes.h" 13 #include "mozilla/Preferences.h" 14 #include "mozilla/dom/BrowserChild.h" 15 #include "mozilla/dom/BrowserParent.h" 16 #include "mozilla/dom/ContentChild.h" 17 #include "mozilla/dom/ContentParent.h" 18 #include "mozilla/dom/Document.h" 19 #include "mozilla/dom/Element.h" 20 #include "mozilla/dom/Event.h" 21 #include "mozilla/dom/PContentPermission.h" 22 #include "mozilla/dom/PContentPermissionRequestParent.h" 23 #include "mozilla/dom/PermissionMessageUtils.h" 24 #include "mozilla/dom/ScriptSettings.h" 25 #include "nsArrayUtils.h" 26 #include "nsCOMPtr.h" 27 #include "nsComponentManagerUtils.h" 28 #include "nsGlobalWindowInner.h" 29 #include "nsIMutableArray.h" 30 #include "nsIPrincipal.h" 31 #include "nsISupportsPrimitives.h" 32 #include "nsIWeakReferenceUtils.h" 33 #include "nsJSUtils.h" 34 #include "nsServiceManagerUtils.h" 35 36 using namespace mozilla::dom; 37 using namespace mozilla; 38 using DelegateInfo = PermissionDelegateHandler::PermissionDelegateInfo; 39 40 namespace mozilla::dom { 41 42 class ContentPermissionRequestParent : public PContentPermissionRequestParent { 43 public: 44 // @param aIsRequestDelegatedToUnsafeThirdParty see 45 // mIsRequestDelegatedToUnsafeThirdParty. 46 ContentPermissionRequestParent( 47 const nsTArray<PermissionRequest>& aRequests, Element* aElement, 48 nsIPrincipal* aPrincipal, nsIPrincipal* aTopLevelPrincipal, 49 const bool aHasValidTransientUserGestureActivation, 50 const bool aIsRequestDelegatedToUnsafeThirdParty); 51 virtual ~ContentPermissionRequestParent(); 52 53 bool IsBeingDestroyed(); 54 55 nsCOMPtr<nsIPrincipal> mPrincipal; 56 nsCOMPtr<nsIPrincipal> mTopLevelPrincipal; 57 nsCOMPtr<Element> mElement; 58 bool mHasValidTransientUserGestureActivation; 59 60 // See nsIPermissionDelegateHandler.maybeUnsafePermissionDelegate. 61 bool mIsRequestDelegatedToUnsafeThirdParty; 62 63 RefPtr<nsContentPermissionRequestProxy> mProxy; 64 nsTArray<PermissionRequest> mRequests; 65 66 private: 67 // Not MOZ_CAN_RUN_SCRIPT because we can't annotate the thing we override yet. 68 MOZ_CAN_RUN_SCRIPT_BOUNDARY 69 virtual mozilla::ipc::IPCResult Recvprompt() override; 70 virtual mozilla::ipc::IPCResult RecvDestroy() override; 71 virtual void ActorDestroy(ActorDestroyReason why) override; 72 }; 73 74 ContentPermissionRequestParent::ContentPermissionRequestParent( 75 const nsTArray<PermissionRequest>& aRequests, Element* aElement, 76 nsIPrincipal* aPrincipal, nsIPrincipal* aTopLevelPrincipal, 77 const bool aHasValidTransientUserGestureActivation, 78 const bool aIsRequestDelegatedToUnsafeThirdParty) { 79 MOZ_COUNT_CTOR(ContentPermissionRequestParent); 80 81 mPrincipal = aPrincipal; 82 mTopLevelPrincipal = aTopLevelPrincipal; 83 mElement = aElement; 84 mRequests = aRequests.Clone(); 85 mHasValidTransientUserGestureActivation = 86 aHasValidTransientUserGestureActivation; 87 mIsRequestDelegatedToUnsafeThirdParty = aIsRequestDelegatedToUnsafeThirdParty; 88 } 89 90 ContentPermissionRequestParent::~ContentPermissionRequestParent() { 91 MOZ_COUNT_DTOR(ContentPermissionRequestParent); 92 } 93 94 mozilla::ipc::IPCResult ContentPermissionRequestParent::Recvprompt() { 95 mProxy = new nsContentPermissionRequestProxy(this); 96 if (NS_FAILED(mProxy->Init(mRequests))) { 97 RefPtr<nsContentPermissionRequestProxy> proxy(mProxy); 98 proxy->Cancel(); 99 } 100 return IPC_OK(); 101 } 102 103 mozilla::ipc::IPCResult ContentPermissionRequestParent::RecvDestroy() { 104 (void)PContentPermissionRequestParent::Send__delete__(this); 105 return IPC_OK(); 106 } 107 108 void ContentPermissionRequestParent::ActorDestroy(ActorDestroyReason why) { 109 if (mProxy) { 110 mProxy->OnParentDestroyed(); 111 } 112 } 113 114 bool ContentPermissionRequestParent::IsBeingDestroyed() { 115 // When ContentParent::MarkAsDead() is called, we are being destroyed. 116 // It's unsafe to send out any message now. 117 ContentParent* contentParent = static_cast<ContentParent*>(Manager()); 118 return !contentParent->IsAlive(); 119 } 120 121 NS_IMPL_ISUPPORTS(ContentPermissionType, nsIContentPermissionType) 122 123 ContentPermissionType::ContentPermissionType( 124 const nsACString& aType, const nsTArray<nsString>& aOptions) { 125 mType = aType; 126 mOptions = aOptions.Clone(); 127 } 128 129 ContentPermissionType::~ContentPermissionType() = default; 130 131 NS_IMETHODIMP 132 ContentPermissionType::GetType(nsACString& aType) { 133 aType = mType; 134 return NS_OK; 135 } 136 137 NS_IMETHODIMP 138 ContentPermissionType::GetOptions(nsIArray** aOptions) { 139 NS_ENSURE_ARG_POINTER(aOptions); 140 141 *aOptions = nullptr; 142 143 nsresult rv; 144 nsCOMPtr<nsIMutableArray> options = 145 do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); 146 NS_ENSURE_SUCCESS(rv, rv); 147 148 // copy options into JS array 149 for (uint32_t i = 0; i < mOptions.Length(); ++i) { 150 nsCOMPtr<nsISupportsString> isupportsString = 151 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv); 152 NS_ENSURE_SUCCESS(rv, rv); 153 154 rv = isupportsString->SetData(mOptions[i]); 155 NS_ENSURE_SUCCESS(rv, rv); 156 157 rv = options->AppendElement(isupportsString); 158 NS_ENSURE_SUCCESS(rv, rv); 159 } 160 161 options.forget(aOptions); 162 return NS_OK; 163 } 164 165 // nsContentPermissionUtils 166 167 /* static */ 168 uint32_t nsContentPermissionUtils::ConvertPermissionRequestToArray( 169 nsTArray<PermissionRequest>& aSrcArray, nsIMutableArray* aDesArray) { 170 uint32_t len = aSrcArray.Length(); 171 for (uint32_t i = 0; i < len; i++) { 172 RefPtr<ContentPermissionType> cpt = 173 new ContentPermissionType(aSrcArray[i].type(), aSrcArray[i].options()); 174 aDesArray->AppendElement(cpt); 175 } 176 return len; 177 } 178 179 /* static */ 180 void nsContentPermissionUtils::ConvertArrayToPermissionRequest( 181 nsIArray* aSrcArray, nsTArray<PermissionRequest>& aDesArray) { 182 uint32_t len = 0; 183 aSrcArray->GetLength(&len); 184 for (uint32_t i = 0; i < len; i++) { 185 nsCOMPtr<nsIContentPermissionType> cpt = do_QueryElementAt(aSrcArray, i); 186 nsAutoCString type; 187 cpt->GetType(type); 188 189 nsCOMPtr<nsIArray> optionArray; 190 cpt->GetOptions(getter_AddRefs(optionArray)); 191 uint32_t optionsLength = 0; 192 if (optionArray) { 193 optionArray->GetLength(&optionsLength); 194 } 195 nsTArray<nsString> options; 196 for (uint32_t j = 0; j < optionsLength; ++j) { 197 nsCOMPtr<nsISupportsString> isupportsString = 198 do_QueryElementAt(optionArray, j); 199 if (isupportsString) { 200 nsString option; 201 isupportsString->GetData(option); 202 options.AppendElement(option); 203 } 204 } 205 206 aDesArray.AppendElement(PermissionRequest(type, options)); 207 } 208 } 209 210 static std::map<PContentPermissionRequestParent*, TabId>& 211 ContentPermissionRequestParentMap() { 212 MOZ_ASSERT(NS_IsMainThread()); 213 static std::map<PContentPermissionRequestParent*, TabId> 214 sPermissionRequestParentMap; 215 return sPermissionRequestParentMap; 216 } 217 218 static std::map<PContentPermissionRequestChild*, TabId>& 219 ContentPermissionRequestChildMap() { 220 MOZ_ASSERT(NS_IsMainThread()); 221 static std::map<PContentPermissionRequestChild*, TabId> 222 sPermissionRequestChildMap; 223 return sPermissionRequestChildMap; 224 } 225 226 /* static */ 227 nsresult nsContentPermissionUtils::CreatePermissionArray( 228 const nsACString& aType, const nsTArray<nsString>& aOptions, 229 nsIArray** aTypesArray) { 230 nsCOMPtr<nsIMutableArray> types = do_CreateInstance(NS_ARRAY_CONTRACTID); 231 RefPtr<ContentPermissionType> permType = 232 new ContentPermissionType(aType, aOptions); 233 types->AppendElement(permType); 234 types.forget(aTypesArray); 235 236 return NS_OK; 237 } 238 239 /* static */ 240 PContentPermissionRequestParent* 241 nsContentPermissionUtils::CreateContentPermissionRequestParent( 242 const nsTArray<PermissionRequest>& aRequests, Element* aElement, 243 nsIPrincipal* aPrincipal, nsIPrincipal* aTopLevelPrincipal, 244 const bool aHasValidTransientUserGestureActivation, 245 const bool aIsRequestDelegatedToUnsafeThirdParty, const TabId& aTabId) { 246 PContentPermissionRequestParent* parent = new ContentPermissionRequestParent( 247 aRequests, aElement, aPrincipal, aTopLevelPrincipal, 248 aHasValidTransientUserGestureActivation, 249 aIsRequestDelegatedToUnsafeThirdParty); 250 ContentPermissionRequestParentMap()[parent] = aTabId; 251 252 return parent; 253 } 254 255 /* static */ 256 nsresult nsContentPermissionUtils::AskPermission( 257 nsIContentPermissionRequest* aRequest, nsPIDOMWindowInner* aWindow) { 258 // for content process 259 if (XRE_IsContentProcess()) { 260 NS_ENSURE_STATE(aWindow && aWindow->IsCurrentInnerWindow()); 261 262 RefPtr<RemotePermissionRequest> req = 263 new RemotePermissionRequest(aRequest, aWindow); 264 265 MOZ_ASSERT(NS_IsMainThread()); // IPC can only be execute on main thread. 266 267 BrowserChild* child = BrowserChild::GetFrom(aWindow->GetDocShell()); 268 NS_ENSURE_TRUE(child, NS_ERROR_FAILURE); 269 270 nsCOMPtr<nsIArray> typeArray; 271 nsresult rv = aRequest->GetTypes(getter_AddRefs(typeArray)); 272 NS_ENSURE_SUCCESS(rv, rv); 273 274 nsTArray<PermissionRequest> permArray; 275 ConvertArrayToPermissionRequest(typeArray, permArray); 276 277 nsCOMPtr<nsIPrincipal> principal; 278 rv = aRequest->GetPrincipal(getter_AddRefs(principal)); 279 NS_ENSURE_SUCCESS(rv, rv); 280 281 nsCOMPtr<nsIPrincipal> topLevelPrincipal; 282 rv = aRequest->GetTopLevelPrincipal(getter_AddRefs(topLevelPrincipal)); 283 NS_ENSURE_SUCCESS(rv, rv); 284 285 bool hasValidTransientUserGestureActivation; 286 rv = aRequest->GetHasValidTransientUserGestureActivation( 287 &hasValidTransientUserGestureActivation); 288 NS_ENSURE_SUCCESS(rv, rv); 289 290 bool isRequestDelegatedToUnsafeThirdParty; 291 rv = aRequest->GetIsRequestDelegatedToUnsafeThirdParty( 292 &isRequestDelegatedToUnsafeThirdParty); 293 NS_ENSURE_SUCCESS(rv, rv); 294 295 req->IPDLAddRef(); 296 if (!ContentChild::GetSingleton()->SendPContentPermissionRequestConstructor( 297 req, permArray, principal, topLevelPrincipal, 298 hasValidTransientUserGestureActivation, 299 isRequestDelegatedToUnsafeThirdParty, child->GetTabId())) { 300 return NS_ERROR_FAILURE; 301 } 302 ContentPermissionRequestChildMap()[req.get()] = child->GetTabId(); 303 304 req->Sendprompt(); 305 return NS_OK; 306 } 307 308 // for chrome process 309 nsCOMPtr<nsIContentPermissionPrompt> prompt = 310 do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID); 311 if (prompt) { 312 if (NS_FAILED(prompt->Prompt(aRequest))) { 313 return NS_ERROR_FAILURE; 314 } 315 } 316 return NS_OK; 317 } 318 319 /* static */ 320 nsTArray<PContentPermissionRequestParent*> 321 nsContentPermissionUtils::GetContentPermissionRequestParentById( 322 const TabId& aTabId) { 323 nsTArray<PContentPermissionRequestParent*> parentArray; 324 for (auto& it : ContentPermissionRequestParentMap()) { 325 if (it.second == aTabId) { 326 parentArray.AppendElement(it.first); 327 } 328 } 329 330 return parentArray; 331 } 332 333 /* static */ 334 void nsContentPermissionUtils::NotifyRemoveContentPermissionRequestParent( 335 PContentPermissionRequestParent* aParent) { 336 auto it = ContentPermissionRequestParentMap().find(aParent); 337 MOZ_ASSERT(it != ContentPermissionRequestParentMap().end()); 338 339 ContentPermissionRequestParentMap().erase(it); 340 } 341 342 /* static */ 343 nsTArray<PContentPermissionRequestChild*> 344 nsContentPermissionUtils::GetContentPermissionRequestChildById( 345 const TabId& aTabId) { 346 nsTArray<PContentPermissionRequestChild*> childArray; 347 for (auto& it : ContentPermissionRequestChildMap()) { 348 if (it.second == aTabId) { 349 childArray.AppendElement(it.first); 350 } 351 } 352 353 return childArray; 354 } 355 356 /* static */ 357 void nsContentPermissionUtils::NotifyRemoveContentPermissionRequestChild( 358 PContentPermissionRequestChild* aChild) { 359 auto it = ContentPermissionRequestChildMap().find(aChild); 360 MOZ_ASSERT(it != ContentPermissionRequestChildMap().end()); 361 362 ContentPermissionRequestChildMap().erase(it); 363 } 364 365 static nsIPrincipal* GetTopLevelPrincipal(nsPIDOMWindowInner* aWindow) { 366 MOZ_ASSERT(aWindow); 367 368 BrowsingContext* top = aWindow->GetBrowsingContext()->Top(); 369 MOZ_ASSERT(top); 370 371 nsPIDOMWindowOuter* outer = top->GetDOMWindow(); 372 if (!outer) { 373 return nullptr; 374 } 375 376 nsPIDOMWindowInner* inner = outer->GetCurrentInnerWindow(); 377 if (!inner) { 378 return nullptr; 379 } 380 381 return nsGlobalWindowInner::Cast(inner)->GetPrincipal(); 382 } 383 384 NS_IMPL_CYCLE_COLLECTION(ContentPermissionRequestBase, mPrincipal, 385 mTopLevelPrincipal, mWindow) 386 387 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ContentPermissionRequestBase) 388 NS_INTERFACE_MAP_ENTRY_CONCRETE(nsISupports) 389 NS_INTERFACE_MAP_ENTRY_CONCRETE(nsIContentPermissionRequest) 390 NS_INTERFACE_MAP_END 391 392 NS_IMPL_CYCLE_COLLECTING_ADDREF(ContentPermissionRequestBase) 393 NS_IMPL_CYCLE_COLLECTING_RELEASE(ContentPermissionRequestBase) 394 395 ContentPermissionRequestBase::ContentPermissionRequestBase( 396 nsIPrincipal* aPrincipal, nsPIDOMWindowInner* aWindow, 397 const nsACString& aPrefName, const nsACString& aType) 398 : mPrincipal(aPrincipal), 399 mTopLevelPrincipal(aWindow ? ::GetTopLevelPrincipal(aWindow) : nullptr), 400 mWindow(aWindow), 401 mPrefName(aPrefName), 402 mType(aType), 403 mHasValidTransientUserGestureActivation(false), 404 mIsRequestDelegatedToUnsafeThirdParty(false) { 405 if (!aWindow) { 406 return; 407 } 408 409 Document* doc = aWindow->GetExtantDoc(); 410 if (!doc) { 411 return; 412 } 413 414 mHasValidTransientUserGestureActivation = 415 doc->HasValidTransientUserGestureActivation(); 416 417 mPermissionHandler = doc->GetPermissionDelegateHandler(); 418 if (mPermissionHandler) { 419 nsTArray<nsCString> types; 420 types.AppendElement(mType); 421 mPermissionHandler->MaybeUnsafePermissionDelegate( 422 types, &mIsRequestDelegatedToUnsafeThirdParty); 423 } 424 } 425 426 NS_IMETHODIMP 427 ContentPermissionRequestBase::GetPrincipal( 428 nsIPrincipal** aRequestingPrincipal) { 429 NS_IF_ADDREF(*aRequestingPrincipal = mPrincipal); 430 return NS_OK; 431 } 432 433 NS_IMETHODIMP 434 ContentPermissionRequestBase::GetDelegatePrincipal( 435 const nsACString& aType, nsIPrincipal** aRequestingPrincipal) { 436 return PermissionDelegateHandler::GetDelegatePrincipal(aType, this, 437 aRequestingPrincipal); 438 } 439 440 NS_IMETHODIMP 441 ContentPermissionRequestBase::GetIsRequestDelegatedToUnsafeThirdParty( 442 bool* aIsRequestDelegatedToUnsafeThirdParty) { 443 *aIsRequestDelegatedToUnsafeThirdParty = 444 mIsRequestDelegatedToUnsafeThirdParty; 445 return NS_OK; 446 } 447 448 NS_IMETHODIMP 449 ContentPermissionRequestBase::GetTopLevelPrincipal( 450 nsIPrincipal** aRequestingPrincipal) { 451 if (!mTopLevelPrincipal) { 452 *aRequestingPrincipal = nullptr; 453 return NS_OK; 454 } 455 456 NS_IF_ADDREF(*aRequestingPrincipal = mTopLevelPrincipal); 457 return NS_OK; 458 } 459 460 NS_IMETHODIMP 461 ContentPermissionRequestBase::GetWindow(mozIDOMWindow** aRequestingWindow) { 462 NS_IF_ADDREF(*aRequestingWindow = mWindow); 463 return NS_OK; 464 } 465 466 NS_IMETHODIMP 467 ContentPermissionRequestBase::GetElement(Element** aElement) { 468 NS_ENSURE_ARG_POINTER(aElement); 469 *aElement = nullptr; 470 return NS_OK; 471 } 472 473 NS_IMETHODIMP 474 ContentPermissionRequestBase::NotifyShown() { 475 // Default implementation does nothing. 476 // Subclasses can override to perform actions when prompt is shown. 477 return NS_OK; 478 } 479 480 NS_IMETHODIMP 481 ContentPermissionRequestBase::GetHasValidTransientUserGestureActivation( 482 bool* aHasValidTransientUserGestureActivation) { 483 *aHasValidTransientUserGestureActivation = 484 mHasValidTransientUserGestureActivation; 485 return NS_OK; 486 } 487 488 NS_IMETHODIMP 489 ContentPermissionRequestBase::GetTypes(nsIArray** aTypes) { 490 nsTArray<nsString> emptyOptions; 491 return nsContentPermissionUtils::CreatePermissionArray(mType, emptyOptions, 492 aTypes); 493 } 494 495 ContentPermissionRequestBase::PromptResult 496 ContentPermissionRequestBase::CheckPromptPrefs() const { 497 MOZ_ASSERT(!mPrefName.IsEmpty(), 498 "This derived class must support checking pref types"); 499 500 nsAutoCString prefName(mPrefName); 501 prefName.AppendLiteral(".prompt.testing"); 502 if (Preferences::GetBool(PromiseFlatCString(prefName).get(), false)) { 503 prefName.AppendLiteral(".allow"); 504 if (Preferences::GetBool(PromiseFlatCString(prefName).get(), true)) { 505 return PromptResult::Granted; 506 } 507 return PromptResult::Denied; 508 } 509 510 return PromptResult::Pending; 511 } 512 513 bool ContentPermissionRequestBase::CheckPermissionDelegate() const { 514 // There is case that ContentPermissionRequestBase is constructed without 515 // window, then mPermissionHandler will be null. So we only check permission 516 // delegate if we have non-null mPermissionHandler 517 if (mPermissionHandler && 518 !mPermissionHandler->HasPermissionDelegated(mType)) { 519 return false; 520 } 521 522 return true; 523 } 524 525 nsresult ContentPermissionRequestBase::ShowPrompt( 526 ContentPermissionRequestBase::PromptResult& aResult) { 527 if (!CheckPermissionDelegate()) { 528 aResult = PromptResult::Denied; 529 return NS_OK; 530 } 531 532 aResult = CheckPromptPrefs(); 533 534 if (aResult != PromptResult::Pending) { 535 return NS_OK; 536 } 537 538 return nsContentPermissionUtils::AskPermission(this, mWindow); 539 } 540 541 class RequestPromptEvent : public Runnable { 542 public: 543 RequestPromptEvent(ContentPermissionRequestBase* aRequest, 544 nsPIDOMWindowInner* aWindow) 545 : mozilla::Runnable("RequestPromptEvent"), 546 mRequest(aRequest), 547 mWindow(aWindow) {} 548 549 NS_IMETHOD Run() override { 550 nsContentPermissionUtils::AskPermission(mRequest, mWindow); 551 return NS_OK; 552 } 553 554 private: 555 RefPtr<ContentPermissionRequestBase> mRequest; 556 nsCOMPtr<nsPIDOMWindowInner> mWindow; 557 }; 558 559 class RequestAllowEvent : public Runnable { 560 public: 561 RequestAllowEvent(bool allow, ContentPermissionRequestBase* request) 562 : mozilla::Runnable("RequestAllowEvent"), 563 mAllow(allow), 564 mRequest(request) {} 565 566 // Not MOZ_CAN_RUN_SCRIPT because we can't annotate the thing we override yet. 567 MOZ_CAN_RUN_SCRIPT_BOUNDARY 568 NS_IMETHOD Run() override { 569 // MOZ_KnownLive is OK, because we never drop the ref to mRequest. 570 if (mAllow) { 571 MOZ_KnownLive(mRequest)->Allow(JS::UndefinedHandleValue); 572 } else { 573 MOZ_KnownLive(mRequest)->Cancel(); 574 } 575 return NS_OK; 576 } 577 578 private: 579 bool mAllow; 580 RefPtr<ContentPermissionRequestBase> mRequest; 581 }; 582 583 void ContentPermissionRequestBase::RequestDelayedTask( 584 nsIEventTarget* aTarget, 585 ContentPermissionRequestBase::DelayedTaskType aType) { 586 nsCOMPtr<nsIRunnable> r; 587 switch (aType) { 588 case DelayedTaskType::Allow: 589 r = new RequestAllowEvent(true, this); 590 break; 591 case DelayedTaskType::Deny: 592 r = new RequestAllowEvent(false, this); 593 break; 594 default: 595 r = new RequestPromptEvent(this, mWindow); 596 break; 597 } 598 599 aTarget->Dispatch(r.forget()); 600 } 601 602 nsresult TranslateChoices( 603 JS::Handle<JS::Value> aChoices, 604 const nsTArray<PermissionRequest>& aPermissionRequests, 605 nsTArray<PermissionChoice>& aTranslatedChoices) { 606 if (aChoices.isNullOrUndefined()) { 607 // No choice is specified. 608 } else if (aChoices.isObject()) { 609 // Iterate through all permission types. 610 for (uint32_t i = 0; i < aPermissionRequests.Length(); ++i) { 611 nsCString type = aPermissionRequests[i].type(); 612 613 JS::Rooted<JSObject*> obj(RootingCx(), &aChoices.toObject()); 614 // People really shouldn't be passing WindowProxy or Location 615 // objects for the choices here. 616 obj = js::CheckedUnwrapStatic(obj); 617 if (!obj) { 618 return NS_ERROR_FAILURE; 619 } 620 621 AutoJSAPI jsapi; 622 jsapi.Init(); 623 624 JSContext* cx = jsapi.cx(); 625 JSAutoRealm ar(cx, obj); 626 627 JS::Rooted<JS::Value> val(cx); 628 629 if (!JS_GetProperty(cx, obj, type.BeginReading(), &val) || 630 !val.isString()) { 631 // no setting for the permission type, clear exception and skip it 632 jsapi.ClearException(); 633 } else { 634 nsAutoJSString choice; 635 if (!choice.init(cx, val)) { 636 jsapi.ClearException(); 637 return NS_ERROR_FAILURE; 638 } 639 aTranslatedChoices.AppendElement(PermissionChoice(type, choice)); 640 } 641 } 642 } else { 643 MOZ_ASSERT(false, "SelectedChoices should be undefined or an JS object"); 644 return NS_ERROR_FAILURE; 645 } 646 647 return NS_OK; 648 } 649 650 } // namespace mozilla::dom 651 652 nsContentPermissionRequestProxy::nsContentPermissionRequestProxy( 653 ContentPermissionRequestParent* parent) 654 : mParent(parent) { 655 NS_ASSERTION(mParent, "null parent"); 656 } 657 658 nsContentPermissionRequestProxy::~nsContentPermissionRequestProxy() = default; 659 660 nsresult nsContentPermissionRequestProxy::Init( 661 const nsTArray<PermissionRequest>& requests) { 662 mPermissionRequests = requests.Clone(); 663 664 nsCOMPtr<nsIContentPermissionPrompt> prompt = 665 do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID); 666 if (!prompt) { 667 return NS_ERROR_FAILURE; 668 } 669 670 prompt->Prompt(this); 671 return NS_OK; 672 } 673 674 void nsContentPermissionRequestProxy::OnParentDestroyed() { mParent = nullptr; } 675 676 NS_IMPL_ISUPPORTS(nsContentPermissionRequestProxy, nsIContentPermissionRequest) 677 678 NS_IMETHODIMP 679 nsContentPermissionRequestProxy::GetTypes(nsIArray** aTypes) { 680 nsCOMPtr<nsIMutableArray> types = do_CreateInstance(NS_ARRAY_CONTRACTID); 681 if (mozilla::dom::nsContentPermissionUtils::ConvertPermissionRequestToArray( 682 mPermissionRequests, types)) { 683 types.forget(aTypes); 684 return NS_OK; 685 } 686 return NS_ERROR_FAILURE; 687 } 688 689 NS_IMETHODIMP 690 nsContentPermissionRequestProxy::GetWindow(mozIDOMWindow** aRequestingWindow) { 691 NS_ENSURE_ARG_POINTER(aRequestingWindow); 692 *aRequestingWindow = nullptr; // ipc doesn't have a window 693 return NS_OK; 694 } 695 696 NS_IMETHODIMP 697 nsContentPermissionRequestProxy::GetPrincipal( 698 nsIPrincipal** aRequestingPrincipal) { 699 NS_ENSURE_ARG_POINTER(aRequestingPrincipal); 700 if (mParent == nullptr) { 701 return NS_ERROR_FAILURE; 702 } 703 704 NS_IF_ADDREF(*aRequestingPrincipal = mParent->mPrincipal); 705 return NS_OK; 706 } 707 708 NS_IMETHODIMP 709 nsContentPermissionRequestProxy::GetTopLevelPrincipal( 710 nsIPrincipal** aRequestingPrincipal) { 711 NS_ENSURE_ARG_POINTER(aRequestingPrincipal); 712 if (mParent == nullptr) { 713 return NS_ERROR_FAILURE; 714 } 715 716 NS_IF_ADDREF(*aRequestingPrincipal = mParent->mTopLevelPrincipal); 717 return NS_OK; 718 } 719 720 NS_IMETHODIMP 721 nsContentPermissionRequestProxy::GetDelegatePrincipal( 722 const nsACString& aType, nsIPrincipal** aRequestingPrincipal) { 723 NS_ENSURE_ARG_POINTER(aRequestingPrincipal); 724 if (mParent == nullptr) { 725 return NS_ERROR_FAILURE; 726 } 727 728 return PermissionDelegateHandler::GetDelegatePrincipal(aType, this, 729 aRequestingPrincipal); 730 } 731 732 NS_IMETHODIMP 733 nsContentPermissionRequestProxy::GetElement(Element** aRequestingElement) { 734 NS_ENSURE_ARG_POINTER(aRequestingElement); 735 if (mParent == nullptr) { 736 return NS_ERROR_FAILURE; 737 } 738 739 nsCOMPtr<Element> elem = mParent->mElement; 740 elem.forget(aRequestingElement); 741 return NS_OK; 742 } 743 744 NS_IMETHODIMP 745 nsContentPermissionRequestProxy::GetHasValidTransientUserGestureActivation( 746 bool* aHasValidTransientUserGestureActivation) { 747 NS_ENSURE_ARG_POINTER(aHasValidTransientUserGestureActivation); 748 if (mParent == nullptr) { 749 return NS_ERROR_FAILURE; 750 } 751 *aHasValidTransientUserGestureActivation = 752 mParent->mHasValidTransientUserGestureActivation; 753 return NS_OK; 754 } 755 756 NS_IMETHODIMP 757 nsContentPermissionRequestProxy::GetIsRequestDelegatedToUnsafeThirdParty( 758 bool* aIsRequestDelegatedToUnsafeThirdParty) { 759 NS_ENSURE_ARG_POINTER(aIsRequestDelegatedToUnsafeThirdParty); 760 if (mParent == nullptr) { 761 return NS_ERROR_FAILURE; 762 } 763 *aIsRequestDelegatedToUnsafeThirdParty = 764 mParent->mIsRequestDelegatedToUnsafeThirdParty; 765 return NS_OK; 766 } 767 768 NS_IMETHODIMP 769 nsContentPermissionRequestProxy::NotifyShown() { 770 // This is a proxy class that forwards to content process. 771 // The actual NotifyShown() logic is handled by the real implementation 772 // in the content process, so we don't need to do anything here. 773 return NS_OK; 774 } 775 776 NS_IMETHODIMP 777 nsContentPermissionRequestProxy::Cancel() { 778 if (mParent == nullptr) { 779 return NS_ERROR_FAILURE; 780 } 781 782 // Don't send out the delete message when the managing protocol (PBrowser) is 783 // being destroyed and PContentPermissionRequest will soon be. 784 if (mParent->IsBeingDestroyed()) { 785 return NS_ERROR_FAILURE; 786 } 787 788 nsTArray<PermissionChoice> emptyChoices; 789 790 (void)mParent->SendNotifyResult(false, emptyChoices); 791 return NS_OK; 792 } 793 794 NS_IMETHODIMP 795 nsContentPermissionRequestProxy::Allow(JS::Handle<JS::Value> aChoices) { 796 if (mParent == nullptr) { 797 return NS_ERROR_FAILURE; 798 } 799 800 // Don't send out the delete message when the managing protocol (PBrowser) is 801 // being destroyed and PContentPermissionRequest will soon be. 802 if (mParent->IsBeingDestroyed()) { 803 return NS_ERROR_FAILURE; 804 } 805 806 nsTArray<PermissionChoice> choices; 807 nsresult rv = TranslateChoices(aChoices, mPermissionRequests, choices); 808 if (NS_FAILED(rv)) { 809 return rv; 810 } 811 812 (void)mParent->SendNotifyResult(true, choices); 813 return NS_OK; 814 } 815 816 // RemotePermissionRequest 817 818 RemotePermissionRequest::RemotePermissionRequest( 819 nsIContentPermissionRequest* aRequest, nsPIDOMWindowInner* aWindow) 820 : mRequest(aRequest), 821 mWindow(aWindow), 822 mIPCOpen(false), 823 mDestroyed(false) {} 824 825 RemotePermissionRequest::~RemotePermissionRequest() { 826 MOZ_ASSERT( 827 !mIPCOpen, 828 "Protocol must not be open when RemotePermissionRequest is destroyed."); 829 } 830 831 void RemotePermissionRequest::DoCancel() { 832 NS_ASSERTION(mRequest, "We need a request"); 833 nsCOMPtr<nsIContentPermissionRequest> request(mRequest); 834 request->Cancel(); 835 } 836 837 void RemotePermissionRequest::DoAllow(JS::Handle<JS::Value> aChoices) { 838 NS_ASSERTION(mRequest, "We need a request"); 839 nsCOMPtr<nsIContentPermissionRequest> request(mRequest); 840 request->Allow(aChoices); 841 } 842 843 // PContentPermissionRequestChild 844 mozilla::ipc::IPCResult RemotePermissionRequest::RecvNotifyResult( 845 const bool& aAllow, nsTArray<PermissionChoice>&& aChoices) { 846 Destroy(); 847 848 if (aAllow && mWindow->IsCurrentInnerWindow()) { 849 // Use 'undefined' if no choice is provided. 850 if (aChoices.IsEmpty()) { 851 DoAllow(JS::UndefinedHandleValue); 852 return IPC_OK(); 853 } 854 855 // Convert choices to a JS val if any. 856 // {"type1": "choice1", "type2": "choiceA"} 857 AutoJSAPI jsapi; 858 if (NS_WARN_IF(!jsapi.Init(mWindow))) { 859 return IPC_OK(); // This is not an IPC error. 860 } 861 862 JSContext* cx = jsapi.cx(); 863 JS::Rooted<JSObject*> obj(cx); 864 obj = JS_NewPlainObject(cx); 865 for (uint32_t i = 0; i < aChoices.Length(); ++i) { 866 const nsString& choice = aChoices[i].choice(); 867 const nsCString& type = aChoices[i].type(); 868 JS::Rooted<JSString*> jChoice( 869 cx, JS_NewUCStringCopyN(cx, choice.get(), choice.Length())); 870 JS::Rooted<JS::Value> vChoice(cx, StringValue(jChoice)); 871 if (!JS_SetProperty(cx, obj, type.get(), vChoice)) { 872 return IPC_FAIL_NO_REASON(this); 873 } 874 } 875 JS::Rooted<JS::Value> val(cx, JS::ObjectValue(*obj)); 876 DoAllow(val); 877 } else { 878 DoCancel(); 879 } 880 return IPC_OK(); 881 } 882 883 void RemotePermissionRequest::Destroy() { 884 if (!IPCOpen()) { 885 return; 886 } 887 (void)this->SendDestroy(); 888 mDestroyed = true; 889 }