LNAPermissionRequest.cpp (5836B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ 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 file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "LNAPermissionRequest.h" 8 #include "nsGlobalWindowInner.h" 9 #include "mozilla/dom/Document.h" 10 #include "nsPIDOMWindow.h" 11 #include "mozilla/Preferences.h" 12 #include "nsContentUtils.h" 13 #include "mozilla/glean/NetwerkMetrics.h" 14 15 #include "mozilla/dom/WindowGlobalParent.h" 16 #include "nsIIOService.h" 17 #include "nsIOService.h" 18 19 namespace mozilla::net { 20 21 //------------------------------------------------- 22 // LNA Permission Requests 23 //------------------------------------------------- 24 25 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(LNAPermissionRequest, 26 ContentPermissionRequestBase) 27 28 NS_IMPL_CYCLE_COLLECTION_INHERITED(LNAPermissionRequest, 29 ContentPermissionRequestBase) 30 31 LNAPermissionRequest::LNAPermissionRequest(PermissionPromptCallback&& aCallback, 32 nsILoadInfo* aLoadInfo, 33 const nsACString& aType) 34 : dom::ContentPermissionRequestBase( 35 aLoadInfo->GetLoadingPrincipal(), nullptr, 36 (aType.Equals(LOCAL_HOST_PERMISSION_KEY) ? "network.localhost"_ns 37 : "network.localnetwork"_ns), 38 aType), 39 mPermissionPromptCallback(std::move(aCallback)) { 40 MOZ_ASSERT(aLoadInfo); 41 42 aLoadInfo->GetTriggeringPrincipal(getter_AddRefs(mPrincipal)); 43 44 RefPtr<mozilla::dom::BrowsingContext> bc; 45 aLoadInfo->GetBrowsingContext(getter_AddRefs(bc)); 46 if (bc && bc->Top()) { 47 if (bc->Top()->Canonical()) { 48 RefPtr<mozilla::dom::WindowGlobalParent> topWindowGlobal = 49 bc->Top()->Canonical()->GetCurrentWindowGlobal(); 50 if (topWindowGlobal) { 51 mTopLevelPrincipal = topWindowGlobal->DocumentPrincipal(); 52 } 53 } 54 } 55 56 if (!mTopLevelPrincipal) { 57 // this could happen in tests 58 mTopLevelPrincipal = mPrincipal; 59 } 60 61 if (!mPrincipal->Equals(mTopLevelPrincipal)) { 62 // This is a cross origin request from Iframe 63 // Since permission delegation is not implemented yet in the parent process 64 // we need to set this flag to true explicitly and display the origin of the 65 // iframe in the prompt. See Bug 1978550 66 mIsRequestDelegatedToUnsafeThirdParty = true; 67 // permissions for this iframe is limited to the iframe's principal 68 mTopLevelPrincipal = mPrincipal; 69 } 70 71 mLoadInfo = aLoadInfo; 72 73 MOZ_ASSERT(mPrincipal); 74 } 75 76 NS_IMETHODIMP 77 LNAPermissionRequest::GetElement(mozilla::dom::Element** aElement) { 78 NS_ENSURE_ARG_POINTER(aElement); 79 RefPtr<mozilla::dom::BrowsingContext> bc; 80 mLoadInfo->GetBrowsingContext(getter_AddRefs(bc)); 81 if (!bc) { 82 return NS_ERROR_FAILURE; 83 } 84 85 return bc->GetTopFrameElement(aElement); 86 } 87 88 // callback when the permission request is denied 89 NS_IMETHODIMP 90 LNAPermissionRequest::Cancel() { 91 // callback to the http channel on the prompt failure result 92 mPermissionPromptCallback(false, mType, mPromptWasShown); 93 return NS_OK; 94 } 95 96 // callback when the permission request is allowed 97 NS_IMETHODIMP 98 LNAPermissionRequest::Allow(JS::Handle<JS::Value> aChoices) { 99 // callback to the http channel on the prompt success result 100 mPermissionPromptCallback(true, mType, mPromptWasShown); 101 return NS_OK; 102 } 103 104 // callback when the permission prompt is shown 105 NS_IMETHODIMP 106 LNAPermissionRequest::NotifyShown() { 107 // Mark that the prompt was shown to the user 108 mPromptWasShown = true; 109 110 // Record telemetry for permission prompts shown to users 111 if (mType.Equals(LOCAL_HOST_PERMISSION_KEY)) { 112 if (mIsRequestDelegatedToUnsafeThirdParty) { 113 mozilla::glean::networking::local_network_access_prompts_shown 114 .Get("localhost_cross_site"_ns) 115 .Add(1); 116 } else { 117 mozilla::glean::networking::local_network_access_prompts_shown 118 .Get("localhost"_ns) 119 .Add(1); 120 } 121 } else if (mType.Equals(LOCAL_NETWORK_PERMISSION_KEY)) { 122 if (mIsRequestDelegatedToUnsafeThirdParty) { 123 mozilla::glean::networking::local_network_access_prompts_shown 124 .Get("local_network_cross_site"_ns) 125 .Add(1); 126 } else { 127 mozilla::glean::networking::local_network_access_prompts_shown 128 .Get("local_network"_ns) 129 .Add(1); 130 } 131 } 132 133 return NS_OK; 134 } 135 136 nsresult LNAPermissionRequest::RequestPermission() { 137 MOZ_ASSERT(NS_IsMainThread()); 138 // This check always returns true 139 // See Bug 1978550 140 if (!CheckPermissionDelegate()) { 141 return Cancel(); 142 } 143 144 // Check if the domain should skip LNA checks 145 if (mPrincipal && gIOService) { 146 nsAutoCString origin; 147 nsresult rv = mPrincipal->GetAsciiHost(origin); 148 if (NS_SUCCEEDED(rv) && !origin.IsEmpty()) { 149 if (gIOService->ShouldSkipDomainForLNA(origin)) { 150 // Domain is in the skip list, grant permission automatically 151 return Allow(JS::UndefinedHandleValue); 152 } 153 } 154 } 155 156 PromptResult pr = CheckPromptPrefs(); 157 if (pr == PromptResult::Granted) { 158 return Allow(JS::UndefinedHandleValue); 159 } 160 161 if (pr == PromptResult::Denied) { 162 return Cancel(); 163 } 164 165 if (NS_SUCCEEDED( 166 dom::nsContentPermissionUtils::AskPermission(this, mWindow))) { 167 // Here we could be getting synchronous callback from the prompts depending 168 // on whether there is already a permission for this or not. If we have a 169 // permission, we will get a synchronous callback Allow/Deny and async if 170 // we don't have a permission yet and waiting for user permission. 171 return NS_OK; 172 } 173 174 return Cancel(); 175 } 176 177 } // namespace mozilla::net