1 /*
2 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3 * Copyright (C) 2012 Google Inc. All Rights Reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 */
27
28 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
29
30 #include "base/metrics/histogram_macros.h"
31 #include "third_party/blink/public/common/feature_policy/document_policy_features.h"
32 #include "third_party/blink/public/mojom/feature_policy/feature_policy_feature.mojom-blink.h"
33 #include "third_party/blink/public/mojom/feature_policy/policy_disposition.mojom-blink.h"
34 #include "third_party/blink/public/platform/task_type.h"
35 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
36 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
37 #include "third_party/blink/renderer/core/dom/events/event_target.h"
38 #include "third_party/blink/renderer/core/events/error_event.h"
39 #include "third_party/blink/renderer/core/execution_context/agent.h"
40 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_state_observer.h"
41 #include "third_party/blink/renderer/core/fileapi/public_url_manager.h"
42 #include "third_party/blink/renderer/core/frame/csp/execution_context_csp_delegate.h"
43 #include "third_party/blink/renderer/core/inspector/console_message.h"
44 #include "third_party/blink/renderer/core/origin_trials/origin_trial_context.h"
45 #include "third_party/blink/renderer/core/probe/core_probes.h"
46 #include "third_party/blink/renderer/core/script/fetch_client_settings_object_impl.h"
47 #include "third_party/blink/renderer/core/workers/worker_global_scope.h"
48 #include "third_party/blink/renderer/core/workers/worker_thread.h"
49 #include "third_party/blink/renderer/platform/heap/heap.h"
50 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
51 #include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h"
52 #include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
53 #include "third_party/blink/renderer/platform/scheduler/public/event_loop.h"
54 #include "third_party/blink/renderer/platform/weborigin/security_policy.h"
55
56 namespace blink {
57
ExecutionContext(v8::Isolate * isolate)58 ExecutionContext::ExecutionContext(v8::Isolate* isolate)
59 : isolate_(isolate),
60 circular_sequential_id_(0),
61 in_dispatch_error_event_(false),
62 lifecycle_state_(mojom::FrameLifecycleState::kRunning),
63 is_context_destroyed_(false),
64 csp_delegate_(MakeGarbageCollected<ExecutionContextCSPDelegate>(*this)),
65 window_interaction_tokens_(0),
66 referrer_policy_(network::mojom::ReferrerPolicy::kDefault) {}
67
68 ExecutionContext::~ExecutionContext() = default;
69
70 // static
From(const ScriptState * script_state)71 ExecutionContext* ExecutionContext::From(const ScriptState* script_state) {
72 v8::HandleScope scope(script_state->GetIsolate());
73 return ToExecutionContext(script_state->GetContext());
74 }
75
76 // static
From(v8::Local<v8::Context> context)77 ExecutionContext* ExecutionContext::From(v8::Local<v8::Context> context) {
78 return ToExecutionContext(context);
79 }
80
81 // static
ForCurrentRealm(const v8::FunctionCallbackInfo<v8::Value> & info)82 ExecutionContext* ExecutionContext::ForCurrentRealm(
83 const v8::FunctionCallbackInfo<v8::Value>& info) {
84 return ToExecutionContext(info.GetIsolate()->GetCurrentContext());
85 }
86
87 // static
ForRelevantRealm(const v8::FunctionCallbackInfo<v8::Value> & info)88 ExecutionContext* ExecutionContext::ForRelevantRealm(
89 const v8::FunctionCallbackInfo<v8::Value>& info) {
90 return ToExecutionContext(info.Holder()->CreationContext());
91 }
92
SetLifecycleState(mojom::FrameLifecycleState state)93 void ExecutionContext::SetLifecycleState(mojom::FrameLifecycleState state) {
94 DCHECK(lifecycle_state_ != state);
95 lifecycle_state_ = state;
96 context_lifecycle_observer_list_.ForEachObserver(
97 [&](ContextLifecycleObserver* observer) {
98 if (!observer->IsExecutionContextLifecycleObserver())
99 return;
100 ExecutionContextLifecycleObserver* execution_context_observer =
101 static_cast<ExecutionContextLifecycleObserver*>(observer);
102 if (execution_context_observer->ObserverType() !=
103 ExecutionContextLifecycleObserver::kStateObjectType)
104 return;
105 ExecutionContextLifecycleStateObserver* state_observer =
106 static_cast<ExecutionContextLifecycleStateObserver*>(
107 execution_context_observer);
108 #if DCHECK_IS_ON()
109 DCHECK_EQ(state_observer->GetExecutionContext(), this);
110 DCHECK(state_observer->UpdateStateIfNeededCalled());
111 #endif
112 state_observer->ContextLifecycleStateChanged(state);
113 });
114 }
115
NotifyContextDestroyed()116 void ExecutionContext::NotifyContextDestroyed() {
117 is_context_destroyed_ = true;
118 context_lifecycle_observer_list_.ForEachObserver(
119 [](ContextLifecycleObserver* observer) {
120 observer->ContextDestroyed();
121 observer->ObserverListWillBeCleared();
122 });
123 context_lifecycle_observer_list_.Clear();
124 }
125
AddContextLifecycleObserver(ContextLifecycleObserver * observer)126 void ExecutionContext::AddContextLifecycleObserver(
127 ContextLifecycleObserver* observer) {
128 context_lifecycle_observer_list_.AddObserver(observer);
129 }
130
RemoveContextLifecycleObserver(ContextLifecycleObserver * observer)131 void ExecutionContext::RemoveContextLifecycleObserver(
132 ContextLifecycleObserver* observer) {
133 DCHECK(context_lifecycle_observer_list_.HasObserver(observer));
134 context_lifecycle_observer_list_.RemoveObserver(observer);
135 }
136
ContextLifecycleStateObserverCountForTesting() const137 unsigned ExecutionContext::ContextLifecycleStateObserverCountForTesting()
138 const {
139 DCHECK(!context_lifecycle_observer_list_.IsIteratingOverObservers());
140 unsigned lifecycle_state_observers = 0;
141 context_lifecycle_observer_list_.ForEachObserver(
142 [&](ContextLifecycleObserver* observer) {
143 if (!observer->IsExecutionContextLifecycleObserver())
144 return;
145 if (static_cast<ExecutionContextLifecycleObserver*>(observer)
146 ->ObserverType() !=
147 ExecutionContextLifecycleObserver::kStateObjectType)
148 return;
149 lifecycle_state_observers++;
150 });
151 return lifecycle_state_observers;
152 }
153
AddConsoleMessageImpl(mojom::ConsoleMessageSource source,mojom::ConsoleMessageLevel level,const String & message,bool discard_duplicates)154 void ExecutionContext::AddConsoleMessageImpl(mojom::ConsoleMessageSource source,
155 mojom::ConsoleMessageLevel level,
156 const String& message,
157 bool discard_duplicates) {
158 AddConsoleMessage(
159 MakeGarbageCollected<ConsoleMessage>(source, level, message),
160 discard_duplicates);
161 }
162
DispatchErrorEvent(ErrorEvent * error_event,SanitizeScriptErrors sanitize_script_errors)163 void ExecutionContext::DispatchErrorEvent(
164 ErrorEvent* error_event,
165 SanitizeScriptErrors sanitize_script_errors) {
166 if (in_dispatch_error_event_) {
167 pending_exceptions_.push_back(error_event);
168 return;
169 }
170
171 // First report the original exception and only then all the nested ones.
172 if (!DispatchErrorEventInternal(error_event, sanitize_script_errors))
173 ExceptionThrown(error_event);
174
175 if (pending_exceptions_.IsEmpty())
176 return;
177 for (ErrorEvent* e : pending_exceptions_)
178 ExceptionThrown(e);
179 pending_exceptions_.clear();
180 }
181
DispatchErrorEventInternal(ErrorEvent * error_event,SanitizeScriptErrors sanitize_script_errors)182 bool ExecutionContext::DispatchErrorEventInternal(
183 ErrorEvent* error_event,
184 SanitizeScriptErrors sanitize_script_errors) {
185 EventTarget* target = ErrorEventTarget();
186 if (!target)
187 return false;
188
189 if (sanitize_script_errors == SanitizeScriptErrors::kSanitize) {
190 error_event = ErrorEvent::CreateSanitizedError(
191 ToScriptState(this, *error_event->World()));
192 }
193
194 DCHECK(!in_dispatch_error_event_);
195 in_dispatch_error_event_ = true;
196 target->DispatchEvent(*error_event);
197 in_dispatch_error_event_ = false;
198 return error_event->defaultPrevented();
199 }
200
IsContextPaused() const201 bool ExecutionContext::IsContextPaused() const {
202 return lifecycle_state_ != mojom::FrameLifecycleState::kRunning;
203 }
204
CircularSequentialID()205 int ExecutionContext::CircularSequentialID() {
206 ++circular_sequential_id_;
207 if (circular_sequential_id_ > ((1U << 31) - 1U))
208 circular_sequential_id_ = 1;
209
210 return circular_sequential_id_;
211 }
212
GetPublicURLManager()213 PublicURLManager& ExecutionContext::GetPublicURLManager() {
214 if (!public_url_manager_)
215 public_url_manager_ = MakeGarbageCollected<PublicURLManager>(this);
216 return *public_url_manager_;
217 }
218
219 ContentSecurityPolicyDelegate&
GetContentSecurityPolicyDelegate()220 ExecutionContext::GetContentSecurityPolicyDelegate() {
221 return *csp_delegate_;
222 }
223
GetContentSecurityPolicyForWorld()224 ContentSecurityPolicy* ExecutionContext::GetContentSecurityPolicyForWorld() {
225 // Isolated worlds are only relevant for Documents. Hence just return the main
226 // world's content security policy by default.
227 return GetContentSecurityPolicy();
228 }
229
GetSecurityOrigin() const230 const SecurityOrigin* ExecutionContext::GetSecurityOrigin() const {
231 return GetSecurityContext().GetSecurityOrigin();
232 }
233
GetMutableSecurityOrigin()234 SecurityOrigin* ExecutionContext::GetMutableSecurityOrigin() {
235 return GetSecurityContext().GetMutableSecurityOrigin();
236 }
237
GetContentSecurityPolicy() const238 ContentSecurityPolicy* ExecutionContext::GetContentSecurityPolicy() const {
239 return GetSecurityContext().GetContentSecurityPolicy();
240 }
241
GetSandboxFlags() const242 mojom::blink::WebSandboxFlags ExecutionContext::GetSandboxFlags() const {
243 return GetSecurityContext().GetSandboxFlags();
244 }
245
IsSandboxed(mojom::blink::WebSandboxFlags mask) const246 bool ExecutionContext::IsSandboxed(mojom::blink::WebSandboxFlags mask) const {
247 return GetSecurityContext().IsSandboxed(mask);
248 }
249
GetAgentClusterID() const250 const base::UnguessableToken& ExecutionContext::GetAgentClusterID() const {
251 return GetAgent()->cluster_id();
252 }
253
AllowWindowInteraction()254 void ExecutionContext::AllowWindowInteraction() {
255 ++window_interaction_tokens_;
256 }
257
ConsumeWindowInteraction()258 void ExecutionContext::ConsumeWindowInteraction() {
259 if (window_interaction_tokens_ == 0)
260 return;
261 --window_interaction_tokens_;
262 }
263
IsWindowInteractionAllowed() const264 bool ExecutionContext::IsWindowInteractionAllowed() const {
265 return window_interaction_tokens_ > 0;
266 }
267
IsSecureContext(String & error_message) const268 bool ExecutionContext::IsSecureContext(String& error_message) const {
269 if (!IsSecureContext()) {
270 error_message = SecurityOrigin::IsPotentiallyTrustworthyErrorMessage();
271 return false;
272 }
273 return true;
274 }
275
276 // https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer
OutgoingReferrer() const277 String ExecutionContext::OutgoingReferrer() const {
278 // Step 3.1: "If environment's global object is a Window object, then"
279 // This case is implemented in Document::OutgoingReferrer().
280
281 // Step 3.2: "Otherwise, let referrerSource be environment's creation URL."
282 return Url().StrippedForUseAsReferrer();
283 }
284
ParseAndSetReferrerPolicy(const String & policies,bool support_legacy_keywords)285 void ExecutionContext::ParseAndSetReferrerPolicy(const String& policies,
286 bool support_legacy_keywords) {
287 network::mojom::ReferrerPolicy referrer_policy;
288
289 if (!SecurityPolicy::ReferrerPolicyFromHeaderValue(
290 policies,
291 support_legacy_keywords ? kSupportReferrerPolicyLegacyKeywords
292 : kDoNotSupportReferrerPolicyLegacyKeywords,
293 &referrer_policy)) {
294 AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
295 mojom::ConsoleMessageSource::kRendering,
296 mojom::ConsoleMessageLevel::kError,
297 "Failed to set referrer policy: The value '" + policies +
298 "' is not one of " +
299 (support_legacy_keywords
300 ? "'always', 'default', 'never', 'origin-when-crossorigin', "
301 : "") +
302 "'no-referrer', 'no-referrer-when-downgrade', 'origin', "
303 "'origin-when-cross-origin', 'same-origin', 'strict-origin', "
304 "'strict-origin-when-cross-origin', or 'unsafe-url'. The referrer "
305 "policy "
306 "has been left unchanged."));
307 return;
308 }
309
310 SetReferrerPolicy(referrer_policy);
311 }
312
SetReferrerPolicy(network::mojom::ReferrerPolicy referrer_policy)313 void ExecutionContext::SetReferrerPolicy(
314 network::mojom::ReferrerPolicy referrer_policy) {
315 // When a referrer policy has already been set, the latest value takes
316 // precedence.
317 UseCounter::Count(this, WebFeature::kSetReferrerPolicy);
318 if (referrer_policy_ != network::mojom::ReferrerPolicy::kDefault)
319 UseCounter::Count(this, WebFeature::kResetReferrerPolicy);
320
321 referrer_policy_ = referrer_policy;
322 }
323
RemoveURLFromMemoryCache(const KURL & url)324 void ExecutionContext::RemoveURLFromMemoryCache(const KURL& url) {
325 GetMemoryCache()->RemoveURLFromCache(url);
326 }
327
Trace(Visitor * visitor)328 void ExecutionContext::Trace(Visitor* visitor) {
329 visitor->Trace(public_url_manager_);
330 visitor->Trace(pending_exceptions_);
331 visitor->Trace(csp_delegate_);
332 visitor->Trace(timers_);
333 visitor->Trace(context_lifecycle_observer_list_);
334 ContextLifecycleNotifier::Trace(visitor);
335 ConsoleLogger::Trace(visitor);
336 Supplementable<ExecutionContext>::Trace(visitor);
337 }
338
IsSameAgentCluster(const base::UnguessableToken & other_id) const339 bool ExecutionContext::IsSameAgentCluster(
340 const base::UnguessableToken& other_id) const {
341 base::UnguessableToken this_id = GetAgentClusterID();
342 // If the AgentClusterID is empty then it should never be the same (e.g.
343 // currently for worklets).
344 if (this_id.is_empty() || other_id.is_empty())
345 return false;
346 return this_id == other_id;
347 }
348
GetMicrotaskQueue() const349 v8::MicrotaskQueue* ExecutionContext::GetMicrotaskQueue() const {
350 // TODO(keishi): Convert to DCHECK once we assign agents everywhere.
351 if (!GetAgent())
352 return nullptr;
353 DCHECK(GetAgent()->event_loop());
354 return GetAgent()->event_loop()->microtask_queue();
355 }
356
FeatureEnabled(OriginTrialFeature feature) const357 bool ExecutionContext::FeatureEnabled(OriginTrialFeature feature) const {
358 return GetOriginTrialContext() &&
359 GetOriginTrialContext()->IsFeatureEnabled(feature);
360 }
361
CountFeaturePolicyUsage(mojom::WebFeature feature)362 void ExecutionContext::CountFeaturePolicyUsage(mojom::WebFeature feature) {
363 UseCounter::Count(*this, feature);
364 }
365
FeaturePolicyFeatureObserved(mojom::blink::FeaturePolicyFeature feature)366 bool ExecutionContext::FeaturePolicyFeatureObserved(
367 mojom::blink::FeaturePolicyFeature feature) {
368 size_t feature_index = static_cast<size_t>(feature);
369 if (parsed_feature_policies_.size() == 0) {
370 parsed_feature_policies_.resize(
371 static_cast<size_t>(mojom::blink::FeaturePolicyFeature::kMaxValue) + 1);
372 } else if (parsed_feature_policies_[feature_index]) {
373 return true;
374 }
375 parsed_feature_policies_[feature_index] = true;
376 return false;
377 }
378
FeaturePolicyPotentialBehaviourChangeObserved(mojom::blink::FeaturePolicyFeature feature) const379 void ExecutionContext::FeaturePolicyPotentialBehaviourChangeObserved(
380 mojom::blink::FeaturePolicyFeature feature) const {
381 size_t feature_index = static_cast<size_t>(feature);
382 if (feature_policy_behaviour_change_counted_.size() == 0) {
383 feature_policy_behaviour_change_counted_.resize(
384 static_cast<size_t>(mojom::blink::FeaturePolicyFeature::kMaxValue) + 1);
385 } else if (feature_policy_behaviour_change_counted_[feature_index]) {
386 return;
387 }
388 feature_policy_behaviour_change_counted_[feature_index] = true;
389 UMA_HISTOGRAM_ENUMERATION(
390 "Blink.UseCounter.FeaturePolicy.ProposalWouldChangeBehaviour", feature);
391 }
392
IsFeatureEnabled(mojom::blink::FeaturePolicyFeature feature,ReportOptions report_on_failure,const String & message,const String & source_file) const393 bool ExecutionContext::IsFeatureEnabled(
394 mojom::blink::FeaturePolicyFeature feature,
395 ReportOptions report_on_failure,
396 const String& message,
397 const String& source_file) const {
398 PolicyValue threshold_value =
399 PolicyValue::CreateMaxPolicyValue(GetSecurityContext()
400 .GetFeaturePolicy()
401 ->GetFeatureList()
402 .at(feature)
403 .second);
404 return IsFeatureEnabled(feature, threshold_value, report_on_failure, message,
405 source_file);
406 }
407
IsFeatureEnabled(mojom::blink::FeaturePolicyFeature feature,PolicyValue threshold_value,ReportOptions report_on_failure,const String & message,const String & source_file) const408 bool ExecutionContext::IsFeatureEnabled(
409 mojom::blink::FeaturePolicyFeature feature,
410 PolicyValue threshold_value,
411 ReportOptions report_on_failure,
412 const String& message,
413 const String& source_file) const {
414 if (report_on_failure == ReportOptions::kReportOnFailure) {
415 // We are expecting a violation report in case the feature is disabled in
416 // the context. Therefore, this qualifies as a potential violation (i.e.,
417 // if the feature was disabled it would generate a report).
418 CountPotentialFeaturePolicyViolation(feature);
419 }
420
421 bool should_report;
422 bool enabled = GetSecurityContext().IsFeatureEnabled(feature, threshold_value,
423 &should_report);
424
425 if (enabled) {
426 // Report if the proposed header semantics change would have affected the
427 // outcome. (https://crbug.com/937131)
428 const FeaturePolicy* policy = GetSecurityContext().GetFeaturePolicy();
429 url::Origin origin = GetSecurityOrigin()->ToUrlOrigin();
430 if (policy->GetProposedFeatureValueForOrigin(feature, origin) <
431 threshold_value) {
432 // Count that there was a change in this page load.
433 const_cast<ExecutionContext*>(this)->CountUse(
434 WebFeature::kFeaturePolicyProposalWouldChangeBehaviour);
435 // Record the specific feature whose behaviour was changed.
436 FeaturePolicyPotentialBehaviourChangeObserved(feature);
437 }
438 }
439
440 if (should_report && report_on_failure == ReportOptions::kReportOnFailure) {
441 mojom::blink::PolicyDisposition disposition =
442 enabled ? mojom::blink::PolicyDisposition::kReport
443 : mojom::blink::PolicyDisposition::kEnforce;
444 ReportFeaturePolicyViolation(feature, disposition, message, source_file);
445 }
446 return enabled;
447 }
448
IsFeatureEnabled(mojom::blink::DocumentPolicyFeature feature,ReportOptions report_option,const String & message,const String & source_file) const449 bool ExecutionContext::IsFeatureEnabled(
450 mojom::blink::DocumentPolicyFeature feature,
451 ReportOptions report_option,
452 const String& message,
453 const String& source_file) const {
454 DCHECK(GetDocumentPolicyFeatureInfoMap().at(feature).default_value.Type() ==
455 mojom::blink::PolicyValueType::kBool);
456 return IsFeatureEnabled(feature, PolicyValue(true), report_option, message,
457 source_file);
458 }
459
IsFeatureEnabled(mojom::blink::DocumentPolicyFeature feature,PolicyValue threshold_value,ReportOptions report_option,const String & message,const String & source_file) const460 bool ExecutionContext::IsFeatureEnabled(
461 mojom::blink::DocumentPolicyFeature feature,
462 PolicyValue threshold_value,
463 ReportOptions report_option,
464 const String& message,
465 const String& source_file) const {
466 // The default value for any feature should be true unless restricted by
467 // document policy
468 if (!RuntimeEnabledFeatures::DocumentPolicyEnabled(this))
469 return true;
470
471 SecurityContext::FeatureStatus status =
472 GetSecurityContext().IsFeatureEnabled(feature, threshold_value);
473 if (status.should_report &&
474 report_option == ReportOptions::kReportOnFailure) {
475 // If both |enabled| and |should_report| are true, the usage must have
476 // violated the report-only policy, i.e. |disposition| ==
477 // mojom::blink::PolicyDisposition::kReport.
478 ReportDocumentPolicyViolation(
479 feature,
480 status.enabled ? mojom::blink::PolicyDisposition::kReport
481 : mojom::blink::PolicyDisposition::kEnforce,
482 message, source_file);
483 }
484 return status.enabled;
485 }
486
RequireTrustedTypes() const487 bool ExecutionContext::RequireTrustedTypes() const {
488 return GetSecurityContext().TrustedTypesRequiredByPolicy() &&
489 RuntimeEnabledFeatures::TrustedDOMTypesEnabled(this);
490 }
491
addressSpaceForBindings() const492 String ExecutionContext::addressSpaceForBindings() const {
493 switch (GetSecurityContext().AddressSpace()) {
494 case network::mojom::IPAddressSpace::kPublic:
495 case network::mojom::IPAddressSpace::kUnknown:
496 return "public";
497
498 case network::mojom::IPAddressSpace::kPrivate:
499 return "private";
500
501 case network::mojom::IPAddressSpace::kLocal:
502 return "local";
503 }
504 NOTREACHED();
505 return "public";
506 }
507
508 } // namespace blink
509