1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/camera/DOMCameraManager.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,453 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "DOMCameraManager.h" 1.9 +#include "nsDebug.h" 1.10 +#include "jsapi.h" 1.11 +#include "Navigator.h" 1.12 +#include "nsPIDOMWindow.h" 1.13 +#include "mozilla/Services.h" 1.14 +#include "nsContentPermissionHelper.h" 1.15 +#include "nsIObserverService.h" 1.16 +#include "nsIPermissionManager.h" 1.17 +#include "DOMCameraControl.h" 1.18 +#include "nsDOMClassInfo.h" 1.19 +#include "CameraCommon.h" 1.20 +#include "mozilla/dom/BindingUtils.h" 1.21 +#include "mozilla/dom/CameraManagerBinding.h" 1.22 +#include "mozilla/dom/PermissionMessageUtils.h" 1.23 +#include "mozilla/dom/TabChild.h" 1.24 +#include "PCOMContentPermissionRequestChild.h" 1.25 + 1.26 +using namespace mozilla; 1.27 +using namespace mozilla::dom; 1.28 + 1.29 +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(nsDOMCameraManager, mWindow) 1.30 + 1.31 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMCameraManager) 1.32 + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver) 1.33 + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 1.34 + NS_INTERFACE_MAP_ENTRY(nsIObserver) 1.35 + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 1.36 +NS_INTERFACE_MAP_END 1.37 + 1.38 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMCameraManager) 1.39 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMCameraManager) 1.40 + 1.41 +/** 1.42 + * Global camera logging object 1.43 + * 1.44 + * Set the NSPR_LOG_MODULES environment variable to enable logging 1.45 + * in a debug build, e.g. NSPR_LOG_MODULES=Camera:5 1.46 + */ 1.47 +PRLogModuleInfo* 1.48 +GetCameraLog() 1.49 +{ 1.50 + static PRLogModuleInfo *sLog; 1.51 + if (!sLog) { 1.52 + sLog = PR_NewLogModule("Camera"); 1.53 + } 1.54 + return sLog; 1.55 +} 1.56 + 1.57 +WindowTable* nsDOMCameraManager::sActiveWindows = nullptr; 1.58 + 1.59 +nsDOMCameraManager::nsDOMCameraManager(nsPIDOMWindow* aWindow) 1.60 + : mWindowId(aWindow->WindowID()) 1.61 + , mPermission(nsIPermissionManager::DENY_ACTION) 1.62 + , mWindow(aWindow) 1.63 +{ 1.64 + /* member initializers and constructor code */ 1.65 + DOM_CAMERA_LOGT("%s:%d : this=%p, windowId=%llx\n", __func__, __LINE__, this, mWindowId); 1.66 + MOZ_COUNT_CTOR(nsDOMCameraManager); 1.67 + SetIsDOMBinding(); 1.68 +} 1.69 + 1.70 +nsDOMCameraManager::~nsDOMCameraManager() 1.71 +{ 1.72 + /* destructor code */ 1.73 + MOZ_COUNT_DTOR(nsDOMCameraManager); 1.74 + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); 1.75 +} 1.76 + 1.77 +/* static */ 1.78 +void 1.79 +nsDOMCameraManager::GetListOfCameras(nsTArray<nsString>& aList, ErrorResult& aRv) 1.80 +{ 1.81 + aRv = ICameraControl::GetListOfCameras(aList); 1.82 +} 1.83 + 1.84 +/* static */ 1.85 +bool 1.86 +nsDOMCameraManager::HasSupport(JSContext* aCx, JSObject* aGlobal) 1.87 +{ 1.88 + return Navigator::HasCameraSupport(aCx, aGlobal); 1.89 +} 1.90 + 1.91 +/* static */ 1.92 +bool 1.93 +nsDOMCameraManager::CheckPermission(nsPIDOMWindow* aWindow) 1.94 +{ 1.95 + nsCOMPtr<nsIPermissionManager> permMgr = 1.96 + do_GetService(NS_PERMISSIONMANAGER_CONTRACTID); 1.97 + NS_ENSURE_TRUE(permMgr, false); 1.98 + 1.99 + uint32_t permission = nsIPermissionManager::DENY_ACTION; 1.100 + permMgr->TestPermissionFromWindow(aWindow, "camera", &permission); 1.101 + if (permission != nsIPermissionManager::ALLOW_ACTION && 1.102 + permission != nsIPermissionManager::PROMPT_ACTION) { 1.103 + return false; 1.104 + } 1.105 + 1.106 + return true; 1.107 +} 1.108 + 1.109 +/* static */ 1.110 +already_AddRefed<nsDOMCameraManager> 1.111 +nsDOMCameraManager::CreateInstance(nsPIDOMWindow* aWindow) 1.112 +{ 1.113 + // Initialize the shared active window tracker 1.114 + if (!sActiveWindows) { 1.115 + sActiveWindows = new WindowTable(); 1.116 + } 1.117 + 1.118 + nsRefPtr<nsDOMCameraManager> cameraManager = 1.119 + new nsDOMCameraManager(aWindow); 1.120 + 1.121 + nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); 1.122 + obs->AddObserver(cameraManager, "xpcom-shutdown", true); 1.123 + 1.124 + return cameraManager.forget(); 1.125 +} 1.126 + 1.127 +class CameraPermissionRequest : public nsIContentPermissionRequest 1.128 + , public PCOMContentPermissionRequestChild 1.129 + , public nsIRunnable 1.130 +{ 1.131 +public: 1.132 + NS_DECL_CYCLE_COLLECTING_ISUPPORTS 1.133 + NS_DECL_NSICONTENTPERMISSIONREQUEST 1.134 + NS_DECL_NSIRUNNABLE 1.135 + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(CameraPermissionRequest, 1.136 + nsIContentPermissionRequest) 1.137 + 1.138 + CameraPermissionRequest(nsIPrincipal* aPrincipal, 1.139 + nsPIDOMWindow* aWindow, 1.140 + nsRefPtr<nsDOMCameraManager> aManager, 1.141 + uint32_t aCameraId, 1.142 + const CameraConfiguration& aInitialConfig, 1.143 + nsRefPtr<GetCameraCallback> aOnSuccess, 1.144 + nsRefPtr<CameraErrorCallback> aOnError) 1.145 + : mPrincipal(aPrincipal) 1.146 + , mWindow(aWindow) 1.147 + , mCameraManager(aManager) 1.148 + , mCameraId(aCameraId) 1.149 + , mInitialConfig(aInitialConfig) 1.150 + , mOnSuccess(aOnSuccess) 1.151 + , mOnError(aOnError) 1.152 + { 1.153 + } 1.154 + 1.155 + virtual ~CameraPermissionRequest() 1.156 + { 1.157 + } 1.158 + 1.159 + bool Recv__delete__(const bool& aAllow, 1.160 + const InfallibleTArray<PermissionChoice>& choices); 1.161 + 1.162 + void IPDLRelease() 1.163 + { 1.164 + Release(); 1.165 + } 1.166 + 1.167 +protected: 1.168 + nsresult DispatchCallback(uint32_t aPermission); 1.169 + void CallAllow(); 1.170 + void CallCancel(); 1.171 + nsCOMPtr<nsIPrincipal> mPrincipal; 1.172 + nsCOMPtr<nsPIDOMWindow> mWindow; 1.173 + nsRefPtr<nsDOMCameraManager> mCameraManager; 1.174 + uint32_t mCameraId; 1.175 + CameraConfiguration mInitialConfig; 1.176 + nsRefPtr<GetCameraCallback> mOnSuccess; 1.177 + nsRefPtr<CameraErrorCallback> mOnError; 1.178 +}; 1.179 + 1.180 +NS_IMPL_CYCLE_COLLECTION(CameraPermissionRequest, mWindow, mOnSuccess, mOnError) 1.181 + 1.182 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CameraPermissionRequest) 1.183 + NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest) 1.184 + NS_INTERFACE_MAP_ENTRY(nsIRunnable) 1.185 + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest) 1.186 +NS_INTERFACE_MAP_END 1.187 + 1.188 +NS_IMPL_CYCLE_COLLECTING_ADDREF(CameraPermissionRequest) 1.189 +NS_IMPL_CYCLE_COLLECTING_RELEASE(CameraPermissionRequest) 1.190 + 1.191 +NS_IMETHODIMP 1.192 +CameraPermissionRequest::Run() 1.193 +{ 1.194 + if (XRE_GetProcessType() == GeckoProcessType_Content) { 1.195 + TabChild* child = TabChild::GetFrom(mWindow->GetDocShell()); 1.196 + if (!child) { 1.197 + return NS_ERROR_NOT_AVAILABLE; 1.198 + } 1.199 + 1.200 + // Retain a reference so the object isn't deleted without IPDL's knowledge. 1.201 + // Corresponding release occurs in DeallocPContentPermissionRequest. 1.202 + AddRef(); 1.203 + 1.204 + nsTArray<PermissionRequest> permArray; 1.205 + nsTArray<nsString> emptyOptions; 1.206 + permArray.AppendElement(PermissionRequest( 1.207 + NS_LITERAL_CSTRING("camera"), 1.208 + NS_LITERAL_CSTRING("unused"), 1.209 + emptyOptions)); 1.210 + child->SendPContentPermissionRequestConstructor(this, permArray, 1.211 + IPC::Principal(mPrincipal)); 1.212 + 1.213 + Sendprompt(); 1.214 + return NS_OK; 1.215 + } 1.216 + 1.217 + nsCOMPtr<nsIContentPermissionPrompt> prompt = 1.218 + do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID); 1.219 + if (prompt) { 1.220 + prompt->Prompt(this); 1.221 + } 1.222 + 1.223 + return NS_OK; 1.224 +} 1.225 + 1.226 +bool 1.227 +CameraPermissionRequest::Recv__delete__(const bool& aAllow, 1.228 + const InfallibleTArray<PermissionChoice>& choices) 1.229 +{ 1.230 + if (aAllow) { 1.231 + Allow(JS::UndefinedHandleValue); 1.232 + } else { 1.233 + Cancel(); 1.234 + } 1.235 + return true; 1.236 +} 1.237 + 1.238 +NS_IMETHODIMP 1.239 +CameraPermissionRequest::GetPrincipal(nsIPrincipal** aRequestingPrincipal) 1.240 +{ 1.241 + NS_ADDREF(*aRequestingPrincipal = mPrincipal); 1.242 + return NS_OK; 1.243 +} 1.244 + 1.245 +NS_IMETHODIMP 1.246 +CameraPermissionRequest::GetWindow(nsIDOMWindow** aRequestingWindow) 1.247 +{ 1.248 + NS_ADDREF(*aRequestingWindow = mWindow); 1.249 + return NS_OK; 1.250 +} 1.251 + 1.252 +NS_IMETHODIMP 1.253 +CameraPermissionRequest::GetElement(nsIDOMElement** aElement) 1.254 +{ 1.255 + *aElement = nullptr; 1.256 + return NS_OK; 1.257 +} 1.258 + 1.259 +NS_IMETHODIMP 1.260 +CameraPermissionRequest::Cancel() 1.261 +{ 1.262 + return DispatchCallback(nsIPermissionManager::DENY_ACTION); 1.263 +} 1.264 + 1.265 +NS_IMETHODIMP 1.266 +CameraPermissionRequest::Allow(JS::HandleValue aChoices) 1.267 +{ 1.268 + MOZ_ASSERT(aChoices.isUndefined()); 1.269 + return DispatchCallback(nsIPermissionManager::ALLOW_ACTION); 1.270 +} 1.271 + 1.272 +nsresult 1.273 +CameraPermissionRequest::DispatchCallback(uint32_t aPermission) 1.274 +{ 1.275 + nsCOMPtr<nsIRunnable> callbackRunnable; 1.276 + if (aPermission == nsIPermissionManager::ALLOW_ACTION) { 1.277 + callbackRunnable = NS_NewRunnableMethod(this, &CameraPermissionRequest::CallAllow); 1.278 + } else { 1.279 + callbackRunnable = NS_NewRunnableMethod(this, &CameraPermissionRequest::CallCancel); 1.280 + } 1.281 + return NS_DispatchToMainThread(callbackRunnable); 1.282 +} 1.283 + 1.284 +void 1.285 +CameraPermissionRequest::CallAllow() 1.286 +{ 1.287 + mCameraManager->PermissionAllowed(mCameraId, mInitialConfig, mOnSuccess, mOnError); 1.288 +} 1.289 + 1.290 +void 1.291 +CameraPermissionRequest::CallCancel() 1.292 +{ 1.293 + mCameraManager->PermissionCancelled(mCameraId, mInitialConfig, mOnSuccess, mOnError); 1.294 +} 1.295 + 1.296 +NS_IMETHODIMP 1.297 +CameraPermissionRequest::GetTypes(nsIArray** aTypes) 1.298 +{ 1.299 + nsTArray<nsString> emptyOptions; 1.300 + return CreatePermissionArray(NS_LITERAL_CSTRING("camera"), 1.301 + NS_LITERAL_CSTRING("unused"), 1.302 + emptyOptions, 1.303 + aTypes); 1.304 +} 1.305 + 1.306 +void 1.307 +nsDOMCameraManager::GetCamera(const nsAString& aCamera, 1.308 + const CameraConfiguration& aInitialConfig, 1.309 + GetCameraCallback& aOnSuccess, 1.310 + const OptionalNonNullCameraErrorCallback& aOnError, 1.311 + ErrorResult& aRv) 1.312 +{ 1.313 + DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); 1.314 + 1.315 + uint32_t cameraId = 0; // back (or forward-facing) camera by default 1.316 + if (aCamera.EqualsLiteral("front")) { 1.317 + cameraId = 1; 1.318 + } 1.319 + 1.320 + nsRefPtr<CameraErrorCallback> errorCallback = nullptr; 1.321 + if (aOnError.WasPassed()) { 1.322 + errorCallback = &aOnError.Value(); 1.323 + } 1.324 + 1.325 + if (mPermission == nsIPermissionManager::ALLOW_ACTION) { 1.326 + PermissionAllowed(cameraId, aInitialConfig, &aOnSuccess, errorCallback); 1.327 + return; 1.328 + } 1.329 + 1.330 + nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(mWindow); 1.331 + if (!sop) { 1.332 + aRv.Throw(NS_ERROR_UNEXPECTED); 1.333 + return; 1.334 + } 1.335 + 1.336 + nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal(); 1.337 + 1.338 + nsCOMPtr<nsIRunnable> permissionRequest = 1.339 + new CameraPermissionRequest(principal, mWindow, this, cameraId, aInitialConfig, 1.340 + &aOnSuccess, errorCallback); 1.341 + 1.342 + NS_DispatchToMainThread(permissionRequest); 1.343 +} 1.344 + 1.345 +void 1.346 +nsDOMCameraManager::PermissionAllowed(uint32_t aCameraId, 1.347 + const CameraConfiguration& aInitialConfig, 1.348 + GetCameraCallback* aOnSuccess, 1.349 + CameraErrorCallback* aOnError) 1.350 +{ 1.351 + mPermission = nsIPermissionManager::ALLOW_ACTION; 1.352 + 1.353 + // Creating this object will trigger the aOnSuccess callback 1.354 + // (or the aOnError one, if it fails). 1.355 + nsRefPtr<nsDOMCameraControl> cameraControl = 1.356 + new nsDOMCameraControl(aCameraId, aInitialConfig, aOnSuccess, aOnError, mWindow); 1.357 + 1.358 + Register(cameraControl); 1.359 +} 1.360 + 1.361 +void 1.362 +nsDOMCameraManager::PermissionCancelled(uint32_t aCameraId, 1.363 + const CameraConfiguration& aInitialConfig, 1.364 + GetCameraCallback* aOnSuccess, 1.365 + CameraErrorCallback* aOnError) 1.366 +{ 1.367 + mPermission = nsIPermissionManager::DENY_ACTION; 1.368 + 1.369 + if (aOnError) { 1.370 + ErrorResult ignored; 1.371 + aOnError->Call(NS_LITERAL_STRING("Permission denied."), ignored); 1.372 + } 1.373 +} 1.374 + 1.375 +void 1.376 +nsDOMCameraManager::Register(nsDOMCameraControl* aDOMCameraControl) 1.377 +{ 1.378 + DOM_CAMERA_LOGI(">>> Register( aDOMCameraControl = %p ) mWindowId = 0x%llx\n", aDOMCameraControl, mWindowId); 1.379 + MOZ_ASSERT(NS_IsMainThread()); 1.380 + 1.381 + // Put the camera control into the hash table 1.382 + CameraControls* controls = sActiveWindows->Get(mWindowId); 1.383 + if (!controls) { 1.384 + controls = new CameraControls; 1.385 + sActiveWindows->Put(mWindowId, controls); 1.386 + } 1.387 + controls->AppendElement(aDOMCameraControl); 1.388 +} 1.389 + 1.390 +void 1.391 +nsDOMCameraManager::Shutdown(uint64_t aWindowId) 1.392 +{ 1.393 + DOM_CAMERA_LOGI(">>> Shutdown( aWindowId = 0x%llx )\n", aWindowId); 1.394 + MOZ_ASSERT(NS_IsMainThread()); 1.395 + 1.396 + CameraControls* controls = sActiveWindows->Get(aWindowId); 1.397 + if (!controls) { 1.398 + return; 1.399 + } 1.400 + 1.401 + uint32_t length = controls->Length(); 1.402 + for (uint32_t i = 0; i < length; i++) { 1.403 + nsRefPtr<nsDOMCameraControl> cameraControl = controls->ElementAt(i); 1.404 + cameraControl->Shutdown(); 1.405 + } 1.406 + controls->Clear(); 1.407 + 1.408 + sActiveWindows->Remove(aWindowId); 1.409 +} 1.410 + 1.411 +void 1.412 +nsDOMCameraManager::XpComShutdown() 1.413 +{ 1.414 + DOM_CAMERA_LOGI(">>> XPCOM Shutdown\n"); 1.415 + MOZ_ASSERT(NS_IsMainThread()); 1.416 + 1.417 + nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); 1.418 + obs->RemoveObserver(this, "xpcom-shutdown"); 1.419 + 1.420 + delete sActiveWindows; 1.421 + sActiveWindows = nullptr; 1.422 +} 1.423 + 1.424 +nsresult 1.425 +nsDOMCameraManager::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) 1.426 +{ 1.427 + if (strcmp(aTopic, "xpcom-shutdown") == 0) { 1.428 + XpComShutdown(); 1.429 + } 1.430 + return NS_OK; 1.431 +} 1.432 + 1.433 +void 1.434 +nsDOMCameraManager::OnNavigation(uint64_t aWindowId) 1.435 +{ 1.436 + DOM_CAMERA_LOGI(">>> OnNavigation event\n"); 1.437 + Shutdown(aWindowId); 1.438 +} 1.439 + 1.440 +bool 1.441 +nsDOMCameraManager::IsWindowStillActive(uint64_t aWindowId) 1.442 +{ 1.443 + MOZ_ASSERT(NS_IsMainThread()); 1.444 + 1.445 + if (!sActiveWindows) { 1.446 + return false; 1.447 + } 1.448 + 1.449 + return !!sActiveWindows->Get(aWindowId); 1.450 +} 1.451 + 1.452 +JSObject* 1.453 +nsDOMCameraManager::WrapObject(JSContext* aCx) 1.454 +{ 1.455 + return CameraManagerBinding::Wrap(aCx, this); 1.456 +}