Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(831)

Unified Diff: android_webview/browser/aw_permission_manager.cc

Issue 2143573002: Implement AwPermissionManager::RequestPermissions (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: allow multiple requests Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: android_webview/browser/aw_permission_manager.cc
diff --git a/android_webview/browser/aw_permission_manager.cc b/android_webview/browser/aw_permission_manager.cc
index a428250f940152f3d242bc52f2892e37674f6cf7..46211a561e62c6881603c1c357fa7f7e4b2e9a35 100644
--- a/android_webview/browser/aw_permission_manager.cc
+++ b/android_webview/browser/aw_permission_manager.cc
@@ -18,8 +18,22 @@
using blink::mojom::PermissionStatus;
using content::PermissionType;
+using RequestPermissionsCallback =
+ base::Callback<void(const std::vector<PermissionStatus>&)>;
+
namespace android_webview {
+namespace {
+
+void PermissionRequestResponseCallbackWrapper(
+ const base::Callback<void(PermissionStatus)>& callback,
+ const std::vector<PermissionStatus>& vector) {
+ DCHECK_EQ(vector.size(), 1ul);
+ callback.Run(vector[0]);
+}
+
+} // namespace
+
class LastRequestResultCache {
public:
LastRequestResultCache() = default;
@@ -131,29 +145,76 @@ class LastRequestResultCache {
DISALLOW_COPY_AND_ASSIGN(LastRequestResultCache);
};
-struct AwPermissionManager::PendingRequest {
+class AwPermissionManager::PendingRequest {
public:
- PendingRequest(PermissionType permission,
+ PendingRequest(const std::vector<PermissionType> permissions,
GURL requesting_origin,
GURL embedding_origin,
- content::RenderFrameHost* render_frame_host,
- const base::Callback<void(PermissionStatus)>& callback)
- : permission(permission),
- requesting_origin(requesting_origin),
- embedding_origin(embedding_origin),
- render_process_id(render_frame_host->GetProcess()->GetID()),
- render_frame_id(render_frame_host->GetRoutingID()),
- callback(callback) {
+ int render_process_id,
+ int render_frame_id,
+ const RequestPermissionsCallback callback)
+ : permissions(permissions),
+ requesting_origin(requesting_origin),
+ embedding_origin(embedding_origin),
+ render_process_id(render_process_id),
+ render_frame_id(render_frame_id),
+ callback(callback),
+ results(permissions.size(), PermissionStatus::DENIED),
+ cancelled_(false) {
+ for (size_t i = 0; i < permissions.size(); ++i)
+ permission_index_map_.insert(std::make_pair(permissions[i], i));
}
~PendingRequest() = default;
- PermissionType permission;
+ void SetPermissionStatus(PermissionType type, PermissionStatus status) {
+ auto result = permission_index_map_.find(type);
+ if (result == permission_index_map_.end()) {
+ NOTREACHED();
+ return;
+ }
+ DCHECK(!IsCompleted());
+ results[result->second] = status;
+ resolved_permissions_.insert(type);
+ }
+
+ PermissionStatus GetPermissionStatus(PermissionType type) {
+ auto result = permission_index_map_.find(type);
+ if (result == permission_index_map_.end()) {
+ NOTREACHED();
+ return PermissionStatus::DENIED;
+ }
+ return results[result->second];
+ }
+
+ bool HasPermissionType(PermissionType type) {
+ return permission_index_map_.find(type) != permission_index_map_.end();
+ }
+
+ bool IsCompleted() const {
+ return results.size() == resolved_permissions_.size();
+ }
+
+ bool IsCompleted(PermissionType type) const {
+ return resolved_permissions_.count(type) != 0;
+ }
+
+ void Cancel() { cancelled_ = true; }
+
+ bool IsCancelled() const { return cancelled_; }
+
+ std::vector<PermissionType> permissions;
GURL requesting_origin;
GURL embedding_origin;
int render_process_id;
int render_frame_id;
- base::Callback<void(PermissionStatus)> callback;
+ RequestPermissionsCallback callback;
+ std::vector<PermissionStatus> results;
+
+ private:
+ std::map<PermissionType, size_t> permission_index_map_;
+ std::set<PermissionType> resolved_permissions_;
+ bool cancelled_;
};
AwPermissionManager::AwPermissionManager()
@@ -163,6 +224,7 @@ AwPermissionManager::AwPermissionManager()
}
AwPermissionManager::~AwPermissionManager() {
+ CancelPermissionRequests();
}
int AwPermissionManager::RequestPermission(
@@ -171,211 +233,245 @@ int AwPermissionManager::RequestPermission(
const GURL& requesting_origin,
bool user_gesture,
const base::Callback<void(PermissionStatus)>& callback) {
- int render_process_id = render_frame_host->GetProcess()->GetID();
- int render_frame_id = render_frame_host->GetRoutingID();
- AwBrowserPermissionRequestDelegate* delegate =
- AwBrowserPermissionRequestDelegate::FromID(render_process_id,
- render_frame_id);
- if (!delegate) {
- DVLOG(0) << "Dropping permission request for "
- << static_cast<int>(permission);
- callback.Run(PermissionStatus::DENIED);
+ return RequestPermissions(
+ std::vector<PermissionType>(1, permission), render_frame_host,
+ requesting_origin, user_gesture,
+ base::Bind(&PermissionRequestResponseCallbackWrapper, callback));
+}
+
+int AwPermissionManager::RequestPermissions(
+ const std::vector<PermissionType>& permissions,
+ content::RenderFrameHost* render_frame_host,
+ const GURL& requesting_origin,
+ bool user_gesture,
+ const base::Callback<void(const std::vector<PermissionStatus>&)>&
+ callback) {
+ if (permissions.empty()) {
+ callback.Run(std::vector<PermissionStatus>());
return kNoPendingOperation;
}
- // Do not delegate any requests which are already pending.
- bool should_delegate_request = true;
- for (PendingRequestsMap::Iterator<PendingRequest> it(&pending_requests_);
- !it.IsAtEnd(); it.Advance()) {
- if (permission == it.GetCurrentValue()->permission) {
- should_delegate_request = false;
- break;
+ const GURL& embedding_origin = LastCommittedOrigin(render_frame_host);
+
+ PendingRequest* pending_request =
+ new PendingRequest(permissions, requesting_origin, embedding_origin,
+ GetRenderProcessID(render_frame_host),
+ GetRenderFrameID(render_frame_host), callback);
+ std::vector<bool> should_delegate_requests =
+ std::vector<bool>(permissions.size(), true);
+ for (size_t i = 0; i < permissions.size(); ++i) {
+ for (PendingRequestsMap::Iterator<PendingRequest> it(&pending_requests_);
+ !it.IsAtEnd(); it.Advance()) {
+ if (it.GetCurrentValue()->HasPermissionType(permissions[i]) &&
+ it.GetCurrentValue()->requesting_origin == requesting_origin) {
+ if (it.GetCurrentValue()->IsCompleted(permissions[i])) {
+ pending_request->SetPermissionStatus(
+ permissions[i],
+ it.GetCurrentValue()->GetPermissionStatus(permissions[i]));
+ }
+ should_delegate_requests[i] = false;
+ break;
+ }
}
}
- const GURL& embedding_origin =
- content::WebContents::FromRenderFrameHost(render_frame_host)
- ->GetLastCommittedURL().GetOrigin();
-
- int request_id = kNoPendingOperation;
- switch (permission) {
- case PermissionType::GEOLOCATION:
- request_id = pending_requests_.Add(new PendingRequest(
- permission, requesting_origin,
- embedding_origin, render_frame_host,
- callback));
- if (should_delegate_request) {
+ int request_id = pending_requests_.Add(pending_request);
+
+ AwBrowserPermissionRequestDelegate* delegate = GetDelegate(
+ pending_request->render_process_id, pending_request->render_frame_id);
+
+ for (size_t i = 0; i < permissions.size(); ++i) {
+ if (!should_delegate_requests[i])
+ continue;
+
+ if (!delegate) {
+ DVLOG(0) << "Dropping permissions request for "
+ << static_cast<int>(permissions[i]);
+ pending_request->SetPermissionStatus(permissions[i],
+ PermissionStatus::DENIED);
+ continue;
+ }
+
+ switch (permissions[i]) {
+ case PermissionType::GEOLOCATION:
delegate->RequestGeolocationPermission(
- requesting_origin,
- base::Bind(&OnRequestResponse,
- weak_ptr_factory_.GetWeakPtr(), request_id,
- callback));
- }
- break;
- case PermissionType::PROTECTED_MEDIA_IDENTIFIER:
- request_id = pending_requests_.Add(new PendingRequest(
- permission, requesting_origin,
- embedding_origin, render_frame_host,
- callback));
- if (should_delegate_request) {
+ pending_request->requesting_origin,
+ base::Bind(&OnRequestResponse, weak_ptr_factory_.GetWeakPtr(),
+ request_id, permissions[i]));
+ break;
+ case PermissionType::PROTECTED_MEDIA_IDENTIFIER:
delegate->RequestProtectedMediaIdentifierPermission(
- requesting_origin,
- base::Bind(&OnRequestResponse,
- weak_ptr_factory_.GetWeakPtr(), request_id,
- callback));
- }
- break;
- case PermissionType::MIDI_SYSEX:
- request_id = pending_requests_.Add(new PendingRequest(
- permission, requesting_origin,
- embedding_origin, render_frame_host,
- callback));
- if (should_delegate_request) {
+ pending_request->requesting_origin,
+ base::Bind(&OnRequestResponse, weak_ptr_factory_.GetWeakPtr(),
+ request_id, permissions[i]));
+ break;
+ case PermissionType::MIDI_SYSEX:
delegate->RequestMIDISysexPermission(
- requesting_origin,
- base::Bind(&OnRequestResponse,
- weak_ptr_factory_.GetWeakPtr(), request_id,
- callback));
- }
- break;
- case PermissionType::AUDIO_CAPTURE:
- case PermissionType::VIDEO_CAPTURE:
- case PermissionType::NOTIFICATIONS:
- case PermissionType::PUSH_MESSAGING:
- case PermissionType::DURABLE_STORAGE:
- case PermissionType::BACKGROUND_SYNC:
- NOTIMPLEMENTED() << "RequestPermission is not implemented for "
- << static_cast<int>(permission);
- callback.Run(PermissionStatus::DENIED);
- break;
- case PermissionType::MIDI:
- callback.Run(PermissionStatus::GRANTED);
- break;
- case PermissionType::NUM:
- NOTREACHED() << "PermissionType::NUM was not expected here.";
- callback.Run(PermissionStatus::DENIED);
- break;
+ pending_request->requesting_origin,
+ base::Bind(&OnRequestResponse, weak_ptr_factory_.GetWeakPtr(),
+ request_id, permissions[i]));
+ break;
+ case PermissionType::AUDIO_CAPTURE:
+ case PermissionType::VIDEO_CAPTURE:
+ case PermissionType::NOTIFICATIONS:
+ case PermissionType::PUSH_MESSAGING:
+ case PermissionType::DURABLE_STORAGE:
+ case PermissionType::BACKGROUND_SYNC:
+ NOTIMPLEMENTED() << "RequestPermissions is not implemented for "
+ << static_cast<int>(permissions[i]);
+ pending_request->SetPermissionStatus(permissions[i],
+ PermissionStatus::DENIED);
+ break;
+ case PermissionType::MIDI:
+ pending_request->SetPermissionStatus(permissions[i],
+ PermissionStatus::GRANTED);
+ break;
+ case PermissionType::NUM:
+ NOTREACHED() << "PermissionType::NUM was not expected here.";
+ pending_request->SetPermissionStatus(permissions[i],
+ PermissionStatus::DENIED);
+ break;
+ }
}
- return request_id;
-}
-int AwPermissionManager::RequestPermissions(
- const std::vector<PermissionType>& permissions,
- content::RenderFrameHost* render_frame_host,
- const GURL& requesting_origin,
- bool user_gesture,
- const base::Callback<void(
- const std::vector<PermissionStatus>&)>& callback) {
- NOTIMPLEMENTED() << "RequestPermissions has not been implemented in WebView";
-
- std::vector<PermissionStatus> result(permissions.size());
- const GURL& embedding_origin =
- content::WebContents::FromRenderFrameHost(render_frame_host)
- ->GetLastCommittedURL().GetOrigin();
-
- for (PermissionType type : permissions) {
- result.push_back(GetPermissionStatus(
- type, requesting_origin, embedding_origin));
+ // If delegate resolve the permission synchronously, all requests could be
+ // already resolved here.
+ if (!pending_requests_.Lookup(request_id))
+ return kNoPendingOperation;
+
+ // If requests are resolved without calling delegate functions, e.g.
+ // PermissionType::MIDI is permitted within the previous for-loop, all
+ // requests could be already resolved, but still in the |pending_requests_|
+ // without invoking the callback.
+ if (pending_request->IsCompleted()) {
+ std::vector<PermissionStatus> results = pending_request->results;
+ pending_requests_.Remove(request_id);
+ callback.Run(results);
+ return kNoPendingOperation;
}
- callback.Run(result);
- return kNoPendingOperation;
+ return request_id;
}
// static
void AwPermissionManager::OnRequestResponse(
const base::WeakPtr<AwPermissionManager>& manager,
int request_id,
- const base::Callback<void(PermissionStatus)>& callback,
+ PermissionType permission,
bool allowed) {
+ // All delegate functions should be cancelled when the manager runs
+ // destructor. Therefore |manager| should be always valid here.
+ DCHECK(manager);
+
PermissionStatus status =
allowed ? PermissionStatus::GRANTED : PermissionStatus::DENIED;
- if (manager.get()) {
- PendingRequest* pending_request =
- manager->pending_requests_.Lookup(request_id);
-
- for (PendingRequestsMap::Iterator<PendingRequest> it(
- &manager->pending_requests_);
- !it.IsAtEnd(); it.Advance()) {
- if (pending_request->permission == it.GetCurrentValue()->permission &&
- it.GetCurrentKey() != request_id) {
- it.GetCurrentValue()->callback.Run(status);
- manager->pending_requests_.Remove(it.GetCurrentKey());
+ PendingRequest* pending_request =
+ manager->pending_requests_.Lookup(request_id);
+
+ manager->result_cache_->SetResult(permission,
+ pending_request->requesting_origin,
+ pending_request->embedding_origin, status);
+
+ std::vector<int> complete_request_ids;
+ std::vector<std::pair<const RequestPermissionsCallback,
+ std::vector<PermissionStatus>>>
+ complete_request_pairs;
+ for (PendingRequestsMap::Iterator<PendingRequest> it(
+ &manager->pending_requests_);
+ !it.IsAtEnd(); it.Advance()) {
+ if (!it.GetCurrentValue()->HasPermissionType(permission) ||
+ it.GetCurrentValue()->requesting_origin !=
+ pending_request->requesting_origin) {
+ continue;
+ }
+ it.GetCurrentValue()->SetPermissionStatus(permission, status);
+ if (it.GetCurrentValue()->IsCompleted()) {
+ complete_request_ids.push_back(it.GetCurrentKey());
+ if (!it.GetCurrentValue()->IsCancelled()) {
+ complete_request_pairs.push_back(std::make_pair(
+ it.GetCurrentValue()->callback, it.GetCurrentValue()->results));
}
}
-
- manager->result_cache_->SetResult(
- pending_request->permission,
- pending_request->requesting_origin,
- pending_request->embedding_origin,
- status);
- manager->pending_requests_.Remove(request_id);
}
- callback.Run(status);
+ for (auto id : complete_request_ids)
+ manager->pending_requests_.Remove(id);
+ for (auto pair : complete_request_pairs)
+ pair.first.Run(pair.second);
}
void AwPermissionManager::CancelPermissionRequest(int request_id) {
PendingRequest* pending_request = pending_requests_.Lookup(request_id);
- if (!pending_request)
+ if (!pending_request || pending_request->IsCancelled())
return;
+ pending_request->Cancel();
- content::RenderFrameHost* render_frame_host =
- content::RenderFrameHost::FromID(pending_request->render_process_id,
- pending_request->render_frame_id);
- content::WebContents* web_contents =
- content::WebContents::FromRenderFrameHost(render_frame_host);
- DCHECK(web_contents);
-
- // The caller is canceling (presumably) the most recent request. Assuming the
- // request did not complete, the user did not respond to the requset.
- // Thus, assume we do not know the result.
- const GURL& embedding_origin = web_contents
- ->GetLastCommittedURL().GetOrigin();
- result_cache_->ClearResult(
- pending_request->permission,
- pending_request->requesting_origin,
- embedding_origin);
-
- AwBrowserPermissionRequestDelegate* delegate =
- AwBrowserPermissionRequestDelegate::FromID(
- pending_request->render_process_id,
- pending_request->render_frame_id);
- if (!delegate) {
- pending_requests_.Remove(request_id);
- return;
- }
+ const GURL& embedding_origin = pending_request->embedding_origin;
+ const GURL& requesting_origin = pending_request->requesting_origin;
+ for (auto permission : pending_request->permissions)
+ result_cache_->ClearResult(permission, requesting_origin, embedding_origin);
+
+ AwBrowserPermissionRequestDelegate* delegate = GetDelegate(
+ pending_request->render_process_id, pending_request->render_frame_id);
- switch (pending_request->permission) {
- case PermissionType::GEOLOCATION:
- delegate->CancelGeolocationPermissionRequests(
- pending_request->requesting_origin);
- break;
- case PermissionType::PROTECTED_MEDIA_IDENTIFIER:
- delegate->CancelProtectedMediaIdentifierPermissionRequests(
- pending_request->requesting_origin);
- break;
- case PermissionType::MIDI_SYSEX:
- delegate->CancelMIDISysexPermissionRequests(
- pending_request->requesting_origin);
- break;
- case PermissionType::NOTIFICATIONS:
- case PermissionType::PUSH_MESSAGING:
- case PermissionType::DURABLE_STORAGE:
- case PermissionType::AUDIO_CAPTURE:
- case PermissionType::VIDEO_CAPTURE:
- case PermissionType::BACKGROUND_SYNC:
- NOTIMPLEMENTED() << "CancelPermission not implemented for "
- << static_cast<int>(pending_request->permission);
- break;
- case PermissionType::MIDI:
- // There is nothing to cancel so this is simply ignored.
- break;
- case PermissionType::NUM:
- NOTREACHED() << "PermissionType::NUM was not expected here.";
- break;
+ for (auto permission : pending_request->permissions) {
+ // If the permission was already resolved, we do not need to cancel it.
+ if (pending_request->IsCompleted(permission))
+ continue;
+
+ // If another pending_request waits for the same permission being resolved,
+ // we should not cancel the delegate's request.
+ bool should_not_cancel_ = false;
+ for (PendingRequestsMap::Iterator<PendingRequest> it(&pending_requests_);
+ !it.IsAtEnd(); it.Advance()) {
+ if (it.GetCurrentValue() != pending_request &&
+ it.GetCurrentValue()->HasPermissionType(permission) &&
+ it.GetCurrentValue()->requesting_origin == requesting_origin &&
+ !it.GetCurrentValue()->IsCompleted(permission)) {
+ should_not_cancel_ = true;
+ break;
+ }
+ }
+ if (should_not_cancel_)
+ continue;
+
+ switch (permission) {
+ case PermissionType::GEOLOCATION:
+ if (delegate)
+ delegate->CancelGeolocationPermissionRequests(requesting_origin);
+ break;
+ case PermissionType::PROTECTED_MEDIA_IDENTIFIER:
+ if (delegate)
+ delegate->CancelProtectedMediaIdentifierPermissionRequests(
+ requesting_origin);
+ break;
+ case PermissionType::MIDI_SYSEX:
+ if (delegate)
+ delegate->CancelMIDISysexPermissionRequests(requesting_origin);
+ break;
+ case PermissionType::NOTIFICATIONS:
+ case PermissionType::PUSH_MESSAGING:
+ case PermissionType::DURABLE_STORAGE:
+ case PermissionType::AUDIO_CAPTURE:
+ case PermissionType::VIDEO_CAPTURE:
+ case PermissionType::BACKGROUND_SYNC:
+ NOTIMPLEMENTED() << "CancelPermission not implemented for "
+ << static_cast<int>(permission);
+ break;
+ case PermissionType::MIDI:
+ // There is nothing to cancel so this is simply ignored.
+ break;
+ case PermissionType::NUM:
+ NOTREACHED() << "PermissionType::NUM was not expected here.";
+ break;
+ }
+ pending_request->SetPermissionStatus(permission, PermissionStatus::DENIED);
}
- pending_requests_.Remove(request_id);
+ // If there are still active requests, we should not remove request_id here,
+ // but just do not invoke a relevant callback when the request is resolved in
+ // OnRequestResponse().
+ if (pending_request->IsCompleted())
+ pending_requests_.Remove(request_id);
}
void AwPermissionManager::ResetPermission(PermissionType permission,
@@ -417,4 +513,37 @@ void AwPermissionManager::UnsubscribePermissionStatusChange(
int subscription_id) {
}
+void AwPermissionManager::CancelPermissionRequests() {
+ std::vector<int> request_ids;
+ for (PendingRequestsMap::Iterator<PendingRequest> it(&pending_requests_);
+ !it.IsAtEnd(); it.Advance()) {
+ request_ids.push_back(it.GetCurrentKey());
+ }
+ for (auto request_id : request_ids)
+ CancelPermissionRequest(request_id);
+ DCHECK(pending_requests_.IsEmpty());
+}
+
+int AwPermissionManager::GetRenderProcessID(
+ content::RenderFrameHost* render_frame_host) {
+ return render_frame_host->GetProcess()->GetID();
+}
+
+int AwPermissionManager::GetRenderFrameID(
+ content::RenderFrameHost* render_frame_host) {
+ return render_frame_host->GetRoutingID();
+}
+
+GURL AwPermissionManager::LastCommittedOrigin(
+ content::RenderFrameHost* render_frame_host) {
+ return content::WebContents::FromRenderFrameHost(render_frame_host)
+ ->GetLastCommittedURL().GetOrigin();
+}
+
+AwBrowserPermissionRequestDelegate* AwPermissionManager::GetDelegate(
+ int render_process_id, int render_frame_id) {
+ return AwBrowserPermissionRequestDelegate::FromID(render_process_id,
+ render_frame_id);
+}
+
} // namespace android_webview
« no previous file with comments | « android_webview/browser/aw_permission_manager.h ('k') | android_webview/browser/aw_permission_manager_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698