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

Side by Side Diff: content/browser/permissions/permission_service_impl.cc

Issue 1771743002: Move geolocation and permission mojoms into WebKit/public/platform. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 9 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 unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/browser/permissions/permission_service_impl.h" 5 #include "content/browser/permissions/permission_service_impl.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 #include <utility> 8 #include <utility>
9 9
10 #include "base/bind.h" 10 #include "base/bind.h"
11 #include "content/public/browser/browser_context.h" 11 #include "content/public/browser/browser_context.h"
12 #include "content/public/browser/permission_manager.h" 12 #include "content/public/browser/permission_manager.h"
13 #include "content/public/browser/permission_type.h" 13 #include "content/public/browser/permission_type.h"
14 14
15 namespace content { 15 namespace content {
16 16
17 namespace { 17 namespace {
18 18
19 PermissionType PermissionNameToPermissionType(PermissionName name) { 19 PermissionType PermissionNameToPermissionType(
20 blink::mojom::PermissionName name) {
20 switch(name) { 21 switch(name) {
21 case PermissionName::GEOLOCATION: 22 case blink::mojom::PermissionName::GEOLOCATION:
22 return PermissionType::GEOLOCATION; 23 return PermissionType::GEOLOCATION;
23 case PermissionName::NOTIFICATIONS: 24 case blink::mojom::PermissionName::NOTIFICATIONS:
24 return PermissionType::NOTIFICATIONS; 25 return PermissionType::NOTIFICATIONS;
25 case PermissionName::PUSH_NOTIFICATIONS: 26 case blink::mojom::PermissionName::PUSH_NOTIFICATIONS:
26 return PermissionType::PUSH_MESSAGING; 27 return PermissionType::PUSH_MESSAGING;
27 case PermissionName::MIDI: 28 case blink::mojom::PermissionName::MIDI:
28 return PermissionType::MIDI; 29 return PermissionType::MIDI;
29 case PermissionName::MIDI_SYSEX: 30 case blink::mojom::PermissionName::MIDI_SYSEX:
30 return PermissionType::MIDI_SYSEX; 31 return PermissionType::MIDI_SYSEX;
31 case PermissionName::PROTECTED_MEDIA_IDENTIFIER: 32 case blink::mojom::PermissionName::PROTECTED_MEDIA_IDENTIFIER:
32 return PermissionType::PROTECTED_MEDIA_IDENTIFIER; 33 return PermissionType::PROTECTED_MEDIA_IDENTIFIER;
33 case PermissionName::DURABLE_STORAGE: 34 case blink::mojom::PermissionName::DURABLE_STORAGE:
34 return PermissionType::DURABLE_STORAGE; 35 return PermissionType::DURABLE_STORAGE;
35 case PermissionName::AUDIO_CAPTURE: 36 case blink::mojom::PermissionName::AUDIO_CAPTURE:
36 return PermissionType::AUDIO_CAPTURE; 37 return PermissionType::AUDIO_CAPTURE;
37 case PermissionName::VIDEO_CAPTURE: 38 case blink::mojom::PermissionName::VIDEO_CAPTURE:
38 return PermissionType::VIDEO_CAPTURE; 39 return PermissionType::VIDEO_CAPTURE;
39 } 40 }
40 41
41 NOTREACHED(); 42 NOTREACHED();
42 return PermissionType::NUM; 43 return PermissionType::NUM;
43 } 44 }
44 45
45 // This function allows the usage of the the multiple request map 46 // This function allows the usage of the the multiple request map
46 // with single requests. 47 // with single requests.
47 void PermissionRequestResponseCallbackWrapper( 48 void PermissionRequestResponseCallbackWrapper(
48 const mojo::Callback<void(PermissionStatus)>& callback, 49 const mojo::Callback<void(blink::mojom::PermissionStatus)>& callback,
49 const mojo::Array<PermissionStatus>& vector) { 50 const mojo::Array<blink::mojom::PermissionStatus>& vector) {
50 DCHECK_EQ(vector.size(), 1ul); 51 DCHECK_EQ(vector.size(), 1ul);
51 callback.Run(vector[0]); 52 callback.Run(vector[0]);
52 } 53 }
53 54
54 } // anonymous namespace 55 } // anonymous namespace
55 56
56 PermissionServiceImpl::PendingRequest::PendingRequest( 57 PermissionServiceImpl::PendingRequest::PendingRequest(
57 const PermissionsStatusCallback& callback, 58 const PermissionsStatusCallback& callback,
58 int request_count) 59 int request_count)
59 : callback(callback), 60 : callback(callback),
60 request_count(request_count) { 61 request_count(request_count) {
61 } 62 }
62 63
63 PermissionServiceImpl::PendingRequest::~PendingRequest() { 64 PermissionServiceImpl::PendingRequest::~PendingRequest() {
64 if (callback.is_null()) 65 if (callback.is_null())
65 return; 66 return;
66 67
67 mojo::Array<PermissionStatus> result = 68 mojo::Array<blink::mojom::PermissionStatus> result =
68 mojo::Array<PermissionStatus>::New(request_count); 69 mojo::Array<blink::mojom::PermissionStatus>::New(request_count);
69 for (int i = 0; i < request_count; ++i) 70 for (int i = 0; i < request_count; ++i)
70 result[i] = PermissionStatus::DENIED; 71 result[i] = blink::mojom::PermissionStatus::DENIED;
71 callback.Run(std::move(result)); 72 callback.Run(std::move(result));
72 } 73 }
73 74
74 PermissionServiceImpl::PendingSubscription::PendingSubscription( 75 PermissionServiceImpl::PendingSubscription::PendingSubscription(
75 PermissionType permission, 76 PermissionType permission,
76 const GURL& origin, 77 const GURL& origin,
77 const PermissionStatusCallback& callback) 78 const PermissionStatusCallback& callback)
78 : id(-1), 79 : id(-1),
79 permission(permission), 80 permission(permission),
80 origin(origin), 81 origin(origin),
81 callback(callback) { 82 callback(callback) {
82 } 83 }
83 84
84 PermissionServiceImpl::PendingSubscription::~PendingSubscription() { 85 PermissionServiceImpl::PendingSubscription::~PendingSubscription() {
85 if (!callback.is_null()) 86 if (!callback.is_null())
86 callback.Run(PermissionStatus::ASK); 87 callback.Run(blink::mojom::PermissionStatus::ASK);
87 } 88 }
88 89
89 PermissionServiceImpl::PermissionServiceImpl( 90 PermissionServiceImpl::PermissionServiceImpl(
90 PermissionServiceContext* context, 91 PermissionServiceContext* context,
91 mojo::InterfaceRequest<PermissionService> request) 92 mojo::InterfaceRequest<blink::mojom::PermissionService> request)
92 : context_(context), 93 : context_(context),
93 binding_(this, std::move(request)), 94 binding_(this, std::move(request)),
94 weak_factory_(this) { 95 weak_factory_(this) {
95 binding_.set_connection_error_handler( 96 binding_.set_connection_error_handler(
96 base::Bind(&PermissionServiceImpl::OnConnectionError, 97 base::Bind(&PermissionServiceImpl::OnConnectionError,
97 base::Unretained(this))); 98 base::Unretained(this)));
98 } 99 }
99 100
100 PermissionServiceImpl::~PermissionServiceImpl() { 101 PermissionServiceImpl::~PermissionServiceImpl() {
101 DCHECK(pending_requests_.IsEmpty()); 102 DCHECK(pending_requests_.IsEmpty());
102 } 103 }
103 104
104 void PermissionServiceImpl::OnConnectionError() { 105 void PermissionServiceImpl::OnConnectionError() {
105 context_->ServiceHadConnectionError(this); 106 context_->ServiceHadConnectionError(this);
106 // After that call, |this| will be deleted. 107 // After that call, |this| will be deleted.
107 } 108 }
108 109
109 void PermissionServiceImpl::RequestPermission( 110 void PermissionServiceImpl::RequestPermission(
110 PermissionName permission, 111 blink::mojom::PermissionName permission,
111 const mojo::String& origin, 112 const mojo::String& origin,
112 const PermissionStatusCallback& callback) { 113 const PermissionStatusCallback& callback) {
113 // This condition is valid if the call is coming from a ChildThread instead of 114 // This condition is valid if the call is coming from a ChildThread instead of
114 // a RenderFrame. Some consumers of the service run in Workers and some in 115 // a RenderFrame. Some consumers of the service run in Workers and some in
115 // Frames. In the context of a Worker, it is not possible to show a 116 // Frames. In the context of a Worker, it is not possible to show a
116 // permission prompt because there is no tab. In the context of a Frame, we 117 // permission prompt because there is no tab. In the context of a Frame, we
117 // can. Even if the call comes from a context where it is not possible to show 118 // can. Even if the call comes from a context where it is not possible to show
118 // any UI, we want to still return something relevant so the current 119 // any UI, we want to still return something relevant so the current
119 // permission status is returned. 120 // permission status is returned.
120 BrowserContext* browser_context = context_->GetBrowserContext(); 121 BrowserContext* browser_context = context_->GetBrowserContext();
(...skipping 18 matching lines...) Expand all
139 // callback if it was run synchronously. 140 // callback if it was run synchronously.
140 PendingRequest* pending_request = pending_requests_.Lookup( 141 PendingRequest* pending_request = pending_requests_.Lookup(
141 pending_request_id); 142 pending_request_id);
142 if (!pending_request) 143 if (!pending_request)
143 return; 144 return;
144 pending_request->id = id; 145 pending_request->id = id;
145 } 146 }
146 147
147 void PermissionServiceImpl::OnRequestPermissionResponse( 148 void PermissionServiceImpl::OnRequestPermissionResponse(
148 int pending_request_id, 149 int pending_request_id,
149 PermissionStatus status) { 150 blink::mojom::PermissionStatus status) {
150 OnRequestPermissionsResponse(pending_request_id, 151 OnRequestPermissionsResponse(
151 std::vector<PermissionStatus>(1, status)); 152 pending_request_id,
153 std::vector<blink::mojom::PermissionStatus>(1, status));
152 } 154 }
153 155
154 void PermissionServiceImpl::RequestPermissions( 156 void PermissionServiceImpl::RequestPermissions(
155 mojo::Array<PermissionName> permissions, 157 mojo::Array<blink::mojom::PermissionName> permissions,
156 const mojo::String& origin, 158 const mojo::String& origin,
157 const PermissionsStatusCallback& callback) { 159 const PermissionsStatusCallback& callback) {
158 if (permissions.is_null()) { 160 if (permissions.is_null()) {
159 callback.Run(mojo::Array<PermissionStatus>()); 161 callback.Run(mojo::Array<blink::mojom::PermissionStatus>());
160 return; 162 return;
161 } 163 }
162 164
163 // This condition is valid if the call is coming from a ChildThread instead of 165 // This condition is valid if the call is coming from a ChildThread instead of
164 // a RenderFrame. Some consumers of the service run in Workers and some in 166 // a RenderFrame. Some consumers of the service run in Workers and some in
165 // Frames. In the context of a Worker, it is not possible to show a 167 // Frames. In the context of a Worker, it is not possible to show a
166 // permission prompt because there is no tab. In the context of a Frame, we 168 // permission prompt because there is no tab. In the context of a Frame, we
167 // can. Even if the call comes from a context where it is not possible to show 169 // can. Even if the call comes from a context where it is not possible to show
168 // any UI, we want to still return something relevant so the current 170 // any UI, we want to still return something relevant so the current
169 // permission status is returned for each permission. 171 // permission status is returned for each permission.
170 BrowserContext* browser_context = context_->GetBrowserContext(); 172 BrowserContext* browser_context = context_->GetBrowserContext();
171 DCHECK(browser_context); 173 DCHECK(browser_context);
172 if (!context_->render_frame_host() || 174 if (!context_->render_frame_host() ||
173 !browser_context->GetPermissionManager()) { 175 !browser_context->GetPermissionManager()) {
174 mojo::Array<PermissionStatus> result(permissions.size()); 176 mojo::Array<blink::mojom::PermissionStatus> result(permissions.size());
175 for (size_t i = 0; i < permissions.size(); ++i) { 177 for (size_t i = 0; i < permissions.size(); ++i) {
176 result[i] = 178 result[i] =
177 GetPermissionStatusFromName(permissions[i], GURL(origin.get())); 179 GetPermissionStatusFromName(permissions[i], GURL(origin.get()));
178 } 180 }
179 callback.Run(std::move(result)); 181 callback.Run(std::move(result));
180 return; 182 return;
181 } 183 }
182 184
183 std::vector<PermissionType> types(permissions.size()); 185 std::vector<PermissionType> types(permissions.size());
184 for (size_t i = 0; i < types.size(); ++i) 186 for (size_t i = 0; i < types.size(); ++i)
(...skipping 13 matching lines...) Expand all
198 // the response callback. 200 // the response callback.
199 PendingRequest* pending_request = pending_requests_.Lookup( 201 PendingRequest* pending_request = pending_requests_.Lookup(
200 pending_request_id); 202 pending_request_id);
201 if (!pending_request) 203 if (!pending_request)
202 return; 204 return;
203 pending_request->id = id; 205 pending_request->id = id;
204 } 206 }
205 207
206 void PermissionServiceImpl::OnRequestPermissionsResponse( 208 void PermissionServiceImpl::OnRequestPermissionsResponse(
207 int pending_request_id, 209 int pending_request_id,
208 const std::vector<PermissionStatus>& result) { 210 const std::vector<blink::mojom::PermissionStatus>& result) {
209 PendingRequest* request = pending_requests_.Lookup(pending_request_id); 211 PendingRequest* request = pending_requests_.Lookup(pending_request_id);
210 PermissionsStatusCallback callback(request->callback); 212 PermissionsStatusCallback callback(request->callback);
211 request->callback.reset(); 213 request->callback.reset();
212 pending_requests_.Remove(pending_request_id); 214 pending_requests_.Remove(pending_request_id);
213 callback.Run(mojo::Array<PermissionStatus>::From(result)); 215 callback.Run(mojo::Array<blink::mojom::PermissionStatus>::From(result));
214 } 216 }
215 217
216 void PermissionServiceImpl::CancelPendingOperations() { 218 void PermissionServiceImpl::CancelPendingOperations() {
217 DCHECK(context_->GetBrowserContext()); 219 DCHECK(context_->GetBrowserContext());
218 220
219 PermissionManager* permission_manager = 221 PermissionManager* permission_manager =
220 context_->GetBrowserContext()->GetPermissionManager(); 222 context_->GetBrowserContext()->GetPermissionManager();
221 if (!permission_manager) 223 if (!permission_manager)
222 return; 224 return;
223 225
(...skipping 11 matching lines...) Expand all
235 it.GetCurrentValue()->callback.Run(GetPermissionStatusFromType( 237 it.GetCurrentValue()->callback.Run(GetPermissionStatusFromType(
236 it.GetCurrentValue()->permission, it.GetCurrentValue()->origin)); 238 it.GetCurrentValue()->permission, it.GetCurrentValue()->origin));
237 it.GetCurrentValue()->callback.reset(); 239 it.GetCurrentValue()->callback.reset();
238 permission_manager->UnsubscribePermissionStatusChange( 240 permission_manager->UnsubscribePermissionStatusChange(
239 it.GetCurrentValue()->id); 241 it.GetCurrentValue()->id);
240 } 242 }
241 pending_subscriptions_.Clear(); 243 pending_subscriptions_.Clear();
242 } 244 }
243 245
244 void PermissionServiceImpl::HasPermission( 246 void PermissionServiceImpl::HasPermission(
245 PermissionName permission, 247 blink::mojom::PermissionName permission,
246 const mojo::String& origin, 248 const mojo::String& origin,
247 const PermissionStatusCallback& callback) { 249 const PermissionStatusCallback& callback) {
248 callback.Run(GetPermissionStatusFromName(permission, GURL(origin.get()))); 250 callback.Run(GetPermissionStatusFromName(permission, GURL(origin.get())));
249 } 251 }
250 252
251 void PermissionServiceImpl::RevokePermission( 253 void PermissionServiceImpl::RevokePermission(
252 PermissionName permission, 254 blink::mojom::PermissionName permission,
253 const mojo::String& origin, 255 const mojo::String& origin,
254 const PermissionStatusCallback& callback) { 256 const PermissionStatusCallback& callback) {
255 GURL origin_url(origin.get()); 257 GURL origin_url(origin.get());
256 PermissionType permission_type = PermissionNameToPermissionType(permission); 258 PermissionType permission_type = PermissionNameToPermissionType(permission);
257 PermissionStatus status = GetPermissionStatusFromType(permission_type, 259 blink::mojom::PermissionStatus status =
258 origin_url); 260 GetPermissionStatusFromType(permission_type, origin_url);
259 261
260 // Resetting the permission should only be possible if the permission is 262 // Resetting the permission should only be possible if the permission is
261 // already granted. 263 // already granted.
262 if (status != PermissionStatus::GRANTED) { 264 if (status != blink::mojom::PermissionStatus::GRANTED) {
263 callback.Run(status); 265 callback.Run(status);
264 return; 266 return;
265 } 267 }
266 268
267 ResetPermissionStatus(permission_type, origin_url); 269 ResetPermissionStatus(permission_type, origin_url);
268 270
269 callback.Run(GetPermissionStatusFromType(permission_type, origin_url)); 271 callback.Run(GetPermissionStatusFromType(permission_type, origin_url));
270 } 272 }
271 273
272 void PermissionServiceImpl::GetNextPermissionChange( 274 void PermissionServiceImpl::GetNextPermissionChange(
273 PermissionName permission, 275 blink::mojom::PermissionName permission,
274 const mojo::String& mojo_origin, 276 const mojo::String& mojo_origin,
275 PermissionStatus last_known_status, 277 blink::mojom::PermissionStatus last_known_status,
276 const PermissionStatusCallback& callback) { 278 const PermissionStatusCallback& callback) {
277 GURL origin(mojo_origin.get()); 279 GURL origin(mojo_origin.get());
278 PermissionStatus current_status = 280 blink::mojom::PermissionStatus current_status =
279 GetPermissionStatusFromName(permission, origin); 281 GetPermissionStatusFromName(permission, origin);
280 if (current_status != last_known_status) { 282 if (current_status != last_known_status) {
281 callback.Run(current_status); 283 callback.Run(current_status);
282 return; 284 return;
283 } 285 }
284 286
285 BrowserContext* browser_context = context_->GetBrowserContext(); 287 BrowserContext* browser_context = context_->GetBrowserContext();
286 DCHECK(browser_context); 288 DCHECK(browser_context);
287 if (!browser_context->GetPermissionManager()) { 289 if (!browser_context->GetPermissionManager()) {
288 callback.Run(current_status); 290 callback.Run(current_status);
(...skipping 14 matching lines...) Expand all
303 browser_context->GetPermissionManager()->SubscribePermissionStatusChange( 305 browser_context->GetPermissionManager()->SubscribePermissionStatusChange(
304 permission_type, 306 permission_type,
305 origin, 307 origin,
306 // If the embedding_origin is empty, we,ll use the |origin| instead. 308 // If the embedding_origin is empty, we,ll use the |origin| instead.
307 embedding_origin.is_empty() ? origin : embedding_origin, 309 embedding_origin.is_empty() ? origin : embedding_origin,
308 base::Bind(&PermissionServiceImpl::OnPermissionStatusChanged, 310 base::Bind(&PermissionServiceImpl::OnPermissionStatusChanged,
309 weak_factory_.GetWeakPtr(), 311 weak_factory_.GetWeakPtr(),
310 pending_subscription_id)); 312 pending_subscription_id));
311 } 313 }
312 314
313 PermissionStatus PermissionServiceImpl::GetPermissionStatusFromName( 315 blink::mojom::PermissionStatus
314 PermissionName permission, const GURL& origin) { 316 PermissionServiceImpl::GetPermissionStatusFromName(
317 blink::mojom::PermissionName permission,
318 const GURL& origin) {
315 return GetPermissionStatusFromType(PermissionNameToPermissionType(permission), 319 return GetPermissionStatusFromType(PermissionNameToPermissionType(permission),
316 origin); 320 origin);
317 } 321 }
318 322
319 PermissionStatus PermissionServiceImpl::GetPermissionStatusFromType( 323 blink::mojom::PermissionStatus
320 PermissionType type, const GURL& origin) { 324 PermissionServiceImpl::GetPermissionStatusFromType(PermissionType type,
325 const GURL& origin) {
321 BrowserContext* browser_context = context_->GetBrowserContext(); 326 BrowserContext* browser_context = context_->GetBrowserContext();
322 DCHECK(browser_context); 327 DCHECK(browser_context);
323 if (!browser_context->GetPermissionManager()) 328 if (!browser_context->GetPermissionManager())
324 return PermissionStatus::DENIED; 329 return blink::mojom::PermissionStatus::DENIED;
325 330
326 // If the embedding_origin is empty we'll use |origin| instead. 331 // If the embedding_origin is empty we'll use |origin| instead.
327 GURL embedding_origin = context_->GetEmbeddingOrigin(); 332 GURL embedding_origin = context_->GetEmbeddingOrigin();
328 return browser_context->GetPermissionManager()->GetPermissionStatus( 333 return browser_context->GetPermissionManager()->GetPermissionStatus(
329 type, origin, embedding_origin.is_empty() ? origin : embedding_origin); 334 type, origin, embedding_origin.is_empty() ? origin : embedding_origin);
330 } 335 }
331 336
332 void PermissionServiceImpl::ResetPermissionStatus(PermissionType type, 337 void PermissionServiceImpl::ResetPermissionStatus(PermissionType type,
333 const GURL& origin) { 338 const GURL& origin) {
334 BrowserContext* browser_context = context_->GetBrowserContext(); 339 BrowserContext* browser_context = context_->GetBrowserContext();
335 DCHECK(browser_context); 340 DCHECK(browser_context);
336 if (!browser_context->GetPermissionManager()) 341 if (!browser_context->GetPermissionManager())
337 return; 342 return;
338 343
339 // If the embedding_origin is empty we'll use |origin| instead. 344 // If the embedding_origin is empty we'll use |origin| instead.
340 GURL embedding_origin = context_->GetEmbeddingOrigin(); 345 GURL embedding_origin = context_->GetEmbeddingOrigin();
341 browser_context->GetPermissionManager()->ResetPermission( 346 browser_context->GetPermissionManager()->ResetPermission(
342 type, origin, embedding_origin.is_empty() ? origin : embedding_origin); 347 type, origin, embedding_origin.is_empty() ? origin : embedding_origin);
343 } 348 }
344 349
345 void PermissionServiceImpl::OnPermissionStatusChanged( 350 void PermissionServiceImpl::OnPermissionStatusChanged(
346 int pending_subscription_id, 351 int pending_subscription_id,
347 PermissionStatus status) { 352 blink::mojom::PermissionStatus status) {
348 PendingSubscription* subscription = 353 PendingSubscription* subscription =
349 pending_subscriptions_.Lookup(pending_subscription_id); 354 pending_subscriptions_.Lookup(pending_subscription_id);
350 355
351 BrowserContext* browser_context = context_->GetBrowserContext(); 356 BrowserContext* browser_context = context_->GetBrowserContext();
352 DCHECK(browser_context); 357 DCHECK(browser_context);
353 if (browser_context->GetPermissionManager()) { 358 if (browser_context->GetPermissionManager()) {
354 browser_context->GetPermissionManager()->UnsubscribePermissionStatusChange( 359 browser_context->GetPermissionManager()->UnsubscribePermissionStatusChange(
355 subscription->id); 360 subscription->id);
356 } 361 }
357 362
358 PermissionStatusCallback callback = subscription->callback; 363 PermissionStatusCallback callback = subscription->callback;
359 364
360 subscription->callback.reset(); 365 subscription->callback.reset();
361 pending_subscriptions_.Remove(pending_subscription_id); 366 pending_subscriptions_.Remove(pending_subscription_id);
362 367
363 callback.Run(status); 368 callback.Run(status);
364 } 369 }
365 370
366 } // namespace content 371 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698