1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/site_instance_impl.h"
#include <string>
#include <tuple>
#include "base/check_is_test.h"
#include "base/containers/contains.h"
#include "base/debug/crash_logging.h"
#include "base/debug/dump_without_crashing.h"
#include "base/lazy_instance.h"
#include "base/notreached.h"
#include "base/trace_event/typed_macros.h"
#include "content/browser/bad_message.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/isolated_origin_util.h"
#include "content/browser/isolation_context.h"
#include "content/browser/process_lock.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/site_instance_group.h"
#include "content/browser/storage_partition_impl.h"
#include "content/common/content_navigation_policy.h"
#include "content/common/features.h"
#include "content/public/browser/browser_or_resource_context.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/site_isolation_policy.h"
#include "content/public/browser/web_ui_controller_factory.h"
#include "content/public/common/content_client.h"
#include "content/public/common/url_constants.h"
#include "content/public/common/url_utils.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "third_party/blink/public/common/chrome_debug_urls.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/perfetto/include/perfetto/tracing/traced_value.h"
#include "url/origin.h"
namespace content {
using perfetto::protos::pbzero::ChromeTrackEvent;
namespace {
// Constant used to mark two call sites that must always agree on whether
// the default SiteInstance is allowed.
constexpr bool kCreateForURLAllowsDefaultSiteInstance = true;
// Asks the embedder whether effective URLs should be used when determining if
// |dest_url| should end up in |site_instance|.
// This is used to keep same-site scripting working for hosted apps.
bool ShouldCompareEffectiveURLs(BrowserContext* browser_context,
SiteInstanceImpl* site_instance,
bool for_outermost_main_frame,
const GURL& dest_url) {
return site_instance->IsDefaultSiteInstance() ||
GetContentClient()
->browser()
->ShouldCompareEffectiveURLsForSiteInstanceSelection(
browser_context, site_instance, for_outermost_main_frame,
site_instance->original_url(), dest_url);
}
SiteInstanceId::Generator g_site_instance_id_generator;
// Produce a crash report stack trace when GetProcess() is called on a
// SiteInstance that does not have a bound process.
// These calls should either be replaced with GetOrCreateProcess() if process
// creation was intentional, or the caller should be changed to avoid
// unnecessarily creating a process.
BASE_FEATURE(kTraceSiteInstanceGetProcessCreation,
"TraceSiteInstanceGetProcessCreation",
base::FEATURE_DISABLED_BY_DEFAULT);
// Whether to crash if GetProcess is called on a SiteInstance without a process.
const base::FeatureParam<bool> kCrashOnGetProcessCreation{
&kTraceSiteInstanceGetProcessCreation, "crash_on_creation", true};
} // namespace
// static
const GURL& SiteInstanceImpl::GetDefaultSiteURL() {
struct DefaultSiteURL {
const GURL url = GURL("http://unisolated.invalid");
};
static base::LazyInstance<DefaultSiteURL>::Leaky default_site_url =
LAZY_INSTANCE_INITIALIZER;
return default_site_url.Get().url;
}
class SiteInstanceImpl::DefaultSiteInstanceState {
public:
void AddSiteInfo(const SiteInfo& site_info) {
default_site_url_set_.insert(site_info.site_url());
}
bool ContainsSite(const GURL& site_url) {
return base::Contains(default_site_url_set_, site_url);
}
private:
// Keeps track of the site URLs that have been mapped to the default
// SiteInstance.
// TODO(wjmaclean): Revise this to store SiteInfos instead of GURLs.
std::set<GURL> default_site_url_set_;
};
SiteInstanceImpl::SiteInstanceImpl(BrowsingInstance* browsing_instance)
: id_(g_site_instance_id_generator.GenerateNextId()),
browsing_instance_(browsing_instance),
can_associate_with_spare_process_(true),
site_info_(browsing_instance->isolation_context()
.browser_or_resource_context()
.ToBrowserContext()),
has_site_(false),
process_reuse_policy_(ProcessReusePolicy::DEFAULT),
is_for_service_worker_(false),
process_assignment_(SiteInstanceProcessAssignment::UNKNOWN) {
DCHECK(browsing_instance);
}
SiteInstanceImpl::~SiteInstanceImpl() {
if (destruction_callback_for_testing_) {
std::move(destruction_callback_for_testing_).Run();
}
// Now that no one is referencing us, we can safely remove ourselves from
// the BrowsingInstance. Any future visits to a page from this site
// (within the same BrowsingInstance) can safely create a new SiteInstance.
if (has_site_)
browsing_instance_->UnregisterSiteInstance(this);
if (has_group()) {
group()->RemoveSiteInstance(this);
ResetSiteInstanceGroup();
}
}
// static
scoped_refptr<SiteInstanceImpl> SiteInstanceImpl::Create(
BrowserContext* browser_context) {
DCHECK(browser_context);
return base::WrapRefCounted(new SiteInstanceImpl(new BrowsingInstance(
browser_context, WebExposedIsolationInfo::CreateNonIsolated(),
/*is_guest=*/false, /*is_fenced=*/false,
/*is_fixed_storage_partition=*/false)));
}
// static
scoped_refptr<SiteInstanceImpl> SiteInstanceImpl::CreateForUrlInfo(
BrowserContext* browser_context,
const UrlInfo& url_info,
bool is_guest,
bool is_fenced,
bool is_fixed_storage_partition) {
TRACE_EVENT("navigation", "SiteInstanceImpl::CreateForUrlInfo", "url_info",
url_info);
DCHECK(url_info.is_sandboxed ||
url_info.unique_sandbox_id == UrlInfo::kInvalidUniqueSandboxId);
CHECK(!is_guest || url_info.storage_partition_config.has_value());
DCHECK(browser_context);
// This will create a new SiteInstance and BrowsingInstance.
scoped_refptr<BrowsingInstance> instance(
new BrowsingInstance(browser_context,
url_info.web_exposed_isolation_info.value_or(
WebExposedIsolationInfo::CreateNonIsolated()),
is_guest, is_fenced, is_fixed_storage_partition));
// Note: The |allow_default_instance| value used here MUST match the value
// used in DoesSiteForURLMatch().
return instance->GetSiteInstanceForURL(
url_info, kCreateForURLAllowsDefaultSiteInstance);
}
// static
scoped_refptr<SiteInstanceImpl> SiteInstanceImpl::CreateForServiceWorker(
BrowserContext* browser_context,
const UrlInfo& url_info,
bool can_reuse_process,
bool is_guest,
bool is_fenced) {
DCHECK(!url_info.url.SchemeIs(kChromeErrorScheme));
DCHECK(url_info.storage_partition_config.has_value());
// This will create a new SiteInstance and BrowsingInstance.
// TODO(crbug.com/40186710): Verify that having different common COOP
// origins does not hinder the ability of a ServiceWorker to share its page's
// process.
scoped_refptr<BrowsingInstance> instance(new BrowsingInstance(
browser_context,
url_info.web_exposed_isolation_info.value_or(
WebExposedIsolationInfo::CreateNonIsolated()),
is_guest, is_fenced,
// It should be safe to just default this to true since the
// BrowsingInstance is not shared with frames, and there are no
// navigations happening in service workers.
/*is_fixed_storage_partition=*/true));
// We do NOT want to allow the default site instance here because workers
// need to be kept separate from other sites.
scoped_refptr<SiteInstanceImpl> site_instance =
instance->GetSiteInstanceForURL(url_info,
/* allow_default_instance */ false);
DCHECK(!site_instance->GetSiteInfo().is_error_page());
DCHECK_EQ(site_instance->IsGuest(), is_guest);
site_instance->is_for_service_worker_ = true;
// Attempt to reuse a renderer process if possible. Note that in the
// <webview> case, process reuse isn't currently supported and a new
// process will always be created (https://crbug.com/752667).
DCHECK(site_instance->process_reuse_policy() == ProcessReusePolicy::DEFAULT ||
site_instance->process_reuse_policy() ==
ProcessReusePolicy::PROCESS_PER_SITE);
if (can_reuse_process) {
site_instance->set_process_reuse_policy(
ProcessReusePolicy::REUSE_PENDING_OR_COMMITTED_SITE_WORKER);
}
return site_instance;
}
// static
scoped_refptr<SiteInstanceImpl> SiteInstanceImpl::CreateForGuest(
BrowserContext* browser_context,
const StoragePartitionConfig& partition_config) {
DCHECK(browser_context);
DCHECK(!partition_config.is_default());
auto guest_site_info =
SiteInfo::CreateForGuest(browser_context, partition_config);
scoped_refptr<SiteInstanceImpl> site_instance =
base::WrapRefCounted(new SiteInstanceImpl(new BrowsingInstance(
browser_context, guest_site_info.web_exposed_isolation_info(),
/*is_guest=*/true,
/*is_fenced=*/false,
/*is_fixed_storage_partition=*/true)));
site_instance->SetSiteInfoInternal(guest_site_info);
return site_instance;
}
// static
scoped_refptr<SiteInstanceImpl>
SiteInstanceImpl::CreateForFixedStoragePartition(
BrowserContext* browser_context,
const GURL& url,
const StoragePartitionConfig& partition_config) {
CHECK(browser_context);
CHECK(!partition_config.is_default());
return SiteInstanceImpl::CreateForUrlInfo(
browser_context,
UrlInfo(UrlInfoInit(url).WithStoragePartitionConfig(partition_config)),
/*is_guest=*/false,
/*is_fenced=*/false, /*is_fixed_storage_partition=*/true);
}
// static
scoped_refptr<SiteInstanceImpl> SiteInstanceImpl::CreateForFencedFrame(
SiteInstanceImpl* embedder_site_instance) {
DCHECK(embedder_site_instance);
BrowserContext* browser_context = embedder_site_instance->GetBrowserContext();
bool should_isolate_fenced_frames =
SiteIsolationPolicy::IsProcessIsolationForFencedFramesEnabled();
scoped_refptr<SiteInstanceImpl> site_instance =
base::WrapRefCounted(new SiteInstanceImpl(new BrowsingInstance(
browser_context, embedder_site_instance->GetWebExposedIsolationInfo(),
embedder_site_instance->IsGuest(),
/*is_fenced=*/should_isolate_fenced_frames,
embedder_site_instance->IsFixedStoragePartition())));
// Give the new fenced frame SiteInstance the same site url as its embedder's
// SiteInstance to allow it to reuse its embedder's process. We avoid doing
// this in the default SiteInstance case as the url will be invalid; process
// reuse will still happen below though, as the embedder's SiteInstance's
// process will not be locked to any site.
// Note: Even when process isolation for fenced frames is enabled, we will
// still be able to reuse the embedder's process below, because we set its
// SiteInfo to be the embedder's SiteInfo, and |is_fenced| will be false. The
// process will change after the first navigation (the new SiteInstance will
// have a SiteInfo with is_fenced set to true).
if (!embedder_site_instance->IsDefaultSiteInstance()) {
site_instance->SetSite(embedder_site_instance->GetSiteInfo());
} else if (embedder_site_instance->IsGuest()) {
// For guests, in the case where the embedder is not a default SiteInstance,
// we reuse the embedder's SiteInfo above. When the embedder is
// a default SiteInstance, we explicitly create a SiteInfo through
// CreateForGuest.
// TODO(crbug.com/40230422): When we support fenced frame process isolation
// with partial or no site isolation modes, we will be able to reach this
// code path and will need to also set is_fenced for the SiteInfo created
// below.
DCHECK(!should_isolate_fenced_frames);
site_instance->SetSite(SiteInfo::CreateForGuest(
browser_context, embedder_site_instance->GetStoragePartitionConfig()));
}
DCHECK_EQ(embedder_site_instance->IsGuest(), site_instance->IsGuest());
if (embedder_site_instance->HasProcess()) {
site_instance->ReuseExistingProcessIfPossible(
embedder_site_instance->GetProcess());
}
return site_instance;
}
// static
scoped_refptr<SiteInstanceImpl>
SiteInstanceImpl::CreateReusableInstanceForTesting(
BrowserContext* browser_context,
const GURL& url) {
DCHECK(browser_context);
// This will create a new SiteInstance and BrowsingInstance.
scoped_refptr<BrowsingInstance> instance(new BrowsingInstance(
browser_context, WebExposedIsolationInfo::CreateNonIsolated(),
/*is_guest=*/false, /*is_fenced=*/false,
/*is_fixed_storage_partition=*/false));
auto site_instance = instance->GetSiteInstanceForURL(
UrlInfo(UrlInfoInit(url)), /* allow_default_instance */ false);
site_instance->set_process_reuse_policy(
ProcessReusePolicy::REUSE_PENDING_OR_COMMITTED_SITE_SUBFRAME);
// Proactively create a process since many callers of this function in tests
// rely on site_instance->GetProcess().
site_instance->GetOrCreateProcess(
ProcessAllocationContext{ProcessAllocationSource::kTest});
return site_instance;
}
// static
scoped_refptr<SiteInstanceImpl> SiteInstanceImpl::CreateForTesting(
BrowserContext* browser_context,
const GURL& url) {
DCHECK(browser_context);
return SiteInstanceImpl::CreateForUrlInfo(
browser_context, UrlInfo::CreateForTesting(url),
/*is_guest=*/false,
/*is_fenced=*/false,
/*is_fixed_storage_partition=*/false);
}
// static
bool SiteInstanceImpl::ShouldAssignSiteForUrlInfo(const UrlInfo& url_info) {
// Only empty document schemes can leave SiteInstances unassigned.
if (!base::Contains(url::GetEmptyDocumentSchemes(), url_info.url.scheme())) {
return true;
}
if (url_info.url.SchemeIs(url::kAboutScheme)) {
// TODO(alexmos): Currently, we force about: URLs that are not about:blank
// to assign a site. This has been the legacy behavior, and it's unclear
// whether this matters one way or another, so we can consider changing
// this if there's a good motivation.
if (!url_info.url.IsAboutBlank()) {
return true;
}
// Check if the UrlInfo carries an inherited origin for about:blank, such
// as with a renderer-initiated about:blank navigation where the origin is
// taken from the navigation's initiator. In such cases, the SiteInstance
// assignment must also honor a valid initiator origin (which could require
// a dedicated process), and hence it cannot be left unassigned.
//
// Note that starting an about:blank navigation from an opaque unique
// origin is safe to leave unassigned. Some Android tests currently rely on
// that behavior.
if (url_info.origin.has_value() &&
url_info.origin->GetTupleOrPrecursorTupleIfOpaque().IsValid()) {
return true;
}
// Otherwise, it is ok for about:blank to not "use up" a new SiteInstance.
// The SiteInstance can still be used for a normal web site. For example,
// this is used for newly-created tabs.
return false;
}
// Do not assign a site for other empty document schemes. One notable use of
// this is for Android's native NTP, which uses the chrome-native: scheme.
return false;
}
SiteInstanceId SiteInstanceImpl::GetId() {
return id_;
}
BrowsingInstanceId SiteInstanceImpl::GetBrowsingInstanceId() {
return browsing_instance_->isolation_context().browsing_instance_id();
}
const IsolationContext& SiteInstanceImpl::GetIsolationContext() {
return browsing_instance_->isolation_context();
}
bool SiteInstanceImpl::IsDefaultSiteInstance() const {
return default_site_instance_state_ != nullptr;
}
void SiteInstanceImpl::AddSiteInfoToDefault(const SiteInfo& site_info) {
DCHECK(!ShouldUseDefaultSiteInstanceGroup());
DCHECK(IsDefaultSiteInstance());
default_site_instance_state_->AddSiteInfo(site_info);
}
bool SiteInstanceImpl::IsSiteInDefaultSiteInstance(const GURL& site_url) const {
DCHECK(IsDefaultSiteInstance());
return default_site_instance_state_->ContainsSite(site_url);
}
SiteInstanceGroup*
SiteInstanceImpl::DefaultSiteInstanceGroupForBrowsingInstance() const {
return browsing_instance()->default_site_instance_group();
}
// static
BrowsingInstanceId SiteInstanceImpl::NextBrowsingInstanceId() {
return BrowsingInstance::NextBrowsingInstanceId();
}
bool SiteInstanceImpl::HasProcess() {
if (has_group())
return true;
// If we would use process-per-site for this site, also check if there is an
// existing process that we would use if GetOrCreateProcess() were called.
if (ShouldUseProcessPerSite() &&
RenderProcessHostImpl::GetSoleProcessHostForSite(GetIsolationContext(),
site_info_)) {
return true;
}
return false;
}
RenderProcessHost* SiteInstanceImpl::GetProcess() {
// TODO(crbug.com/388998723):
// Change this function to either add a CHECK(HasProcess()) or return null if
// there is no bound process after collecting and fixing any
// DumpWithoutCrashing reports.
if (!HasProcess() &&
base::FeatureList::IsEnabled(kTraceSiteInstanceGetProcessCreation)) {
if (kCrashOnGetProcessCreation.Get()) {
CHECK(false);
}
}
return GetOrCreateProcess(ProcessAllocationContext{
ProcessAllocationSource::kNoProcessCreationExpected});
}
RenderProcessHost* SiteInstanceImpl::GetOrCreateProcess(
const ProcessAllocationContext& context) {
if (!HasProcess() &&
base::FeatureList::IsEnabled(kTraceSiteInstanceGetProcessCreation) &&
context.source == ProcessAllocationSource::kNoProcessCreationExpected) {
base::debug::DumpWithoutCrashing();
}
// Create a new SiteInstanceGroup and RenderProcessHost if there isn't one.
// All SiteInstances within a SiteInstanceGroup share a process and
// AgentSchedulingGroupHost. A group must have a process. If the process gets
// destructed, `site_instance_group_` will get cleared, and another one with a
// new process will be assigned the next time GetProcess() gets called.
if (!has_group()) {
// Check if the ProcessReusePolicy should be updated.
if (ShouldUseProcessPerSite()) {
process_reuse_policy_ = ProcessReusePolicy::PROCESS_PER_SITE;
} else if (process_reuse_policy_ == ProcessReusePolicy::PROCESS_PER_SITE) {
process_reuse_policy_ = ProcessReusePolicy::DEFAULT;
}
ProcessAllocationContext allocation_context = context;
if (allocation_context.navigation_context.has_value()) {
allocation_context.navigation_context->requires_new_process_for_coop =
coop_reuse_process_failed_;
}
// See if `this` can be placed in the default SiteInstanceGroup, otherwise
// create a process and associated SiteInstanceGroup.
if (CanPutSiteInstanceInDefaultGroup() &&
browsing_instance_->has_default_site_instance_group()) {
browsing_instance_->default_site_instance_group()->AddSiteInstance(this);
SetSiteInstanceGroup(browsing_instance_->default_site_instance_group());
} else {
SetProcessInternal(RenderProcessHostImpl::GetProcessHostForSiteInstance(
this, allocation_context));
}
}
DCHECK(site_instance_group_);
return site_instance_group_->process();
}
RenderProcessHost* SiteInstanceImpl::GetOrCreateProcess() {
CHECK_IS_TEST();
return GetOrCreateProcess(
ProcessAllocationContext{ProcessAllocationSource::kTest});
}
SiteInstanceGroupId SiteInstanceImpl::GetSiteInstanceGroupId() {
return has_group() ? site_instance_group_->GetId() : SiteInstanceGroupId(0);
}
bool SiteInstanceImpl::ShouldUseProcessPerSite() const {
BrowserContext* browser_context = browsing_instance_->GetBrowserContext();
return has_site_ && site_info_.ShouldUseProcessPerSite(browser_context);
}
void SiteInstanceImpl::ReuseExistingProcessIfPossible(
RenderProcessHost* existing_process) {
if (HasProcess())
return;
// We should not reuse `existing_process` if the destination uses
// process-per-site. Note that this includes the case where the process for
// the site is not there yet (so we're going to create a new process). Note
// also that this does not apply for the reverse case: if the existing process
// is used for a process-per-site site, it is ok to reuse this for the new
// page (regardless of the site).
if (ShouldUseProcessPerSite())
return;
// Do not reuse the process if it's not suitable for this SiteInstance. For
// example, this won't allow reusing a process if it's locked to a site that's
// different from this SiteInstance's site.
if (!RenderProcessHostImpl::MayReuseAndIsSuitable(existing_process, this)) {
return;
}
// If `this` can go in the default SiteInstanceGroup and one exists, prefer
// that SiteInstanceGroup and process.
if (CanPutSiteInstanceInDefaultGroup() &&
browsing_instance()->has_default_site_instance_group()) {
return;
}
// TODO(crbug.com/40676483): Don't try to reuse process if either of the
// SiteInstances are cross-origin isolated (uses COOP/COEP).
SetProcessInternal(existing_process);
}
void SiteInstanceImpl::SetProcessInternal(RenderProcessHost* process) {
if (!site_instance_group_) {
site_instance_group_ = base::WrapRefCounted(
new SiteInstanceGroup(browsing_instance(), process));
site_instance_group_->AddSiteInstance(this);
}
// Check if the process created should become the default SiteInstanceGroup's
// process. If so, set `site_instance_group_` to be the default
// SiteInstanceGroup. We should only get here if a process needs to be created
// for the default SiteInstanceGroup.
if (CanPutSiteInstanceInDefaultGroup()) {
MaybeSetDefaultSiteInstanceGroup();
}
LockProcessIfNeeded();
// If we are using process-per-site, we need to register this process
// for the current site so that we can find it again. (If no site is set
// at this time, we will register it in SetSite().)
if (process_reuse_policy_ == ProcessReusePolicy::PROCESS_PER_SITE &&
has_site_) {
RenderProcessHostImpl::RegisterSoleProcessHostForSite(
site_instance_group_->process(), this);
}
TRACE_EVENT2("navigation", "SiteInstanceImpl::SetProcessInternal", "site id",
id_.value(), "process id",
site_instance_group_->process()->GetDeprecatedID());
// Inform the embedder if the SiteInstance now has both the process and the
// site assigned. Note that this can be called either here or when setting
// the site in SetSiteInfoInternal() below. This could be called multiple
// times if the SiteInstance's RenderProcessHost goes away and a new one
// replaces it later.
if (has_site_) {
GetContentClient()->browser()->SiteInstanceGotProcessAndSite(this);
}
}
bool SiteInstanceImpl::CanAssociateWithSpareProcess() {
return can_associate_with_spare_process_;
}
void SiteInstanceImpl::PreventAssociationWithSpareProcess() {
can_associate_with_spare_process_ = false;
}
void SiteInstanceImpl::SetSite(const UrlInfo& url_info) {
const GURL& url = url_info.url;
// TODO(creis): Consider calling ShouldAssignSiteForURL internally, rather
// than before multiple call sites. See https://crbug.com/949220.
TRACE_EVENT2("navigation", "SiteInstanceImpl::SetSite", "site id",
id_.value(), "url_info", url_info);
// A SiteInstance's site should not change.
// TODO(creis): When following links or script navigations, we can currently
// render pages from other sites in this SiteInstance. This will eventually
// be fixed, but until then, we should still not set the site of a
// SiteInstance more than once.
DCHECK(!has_site_);
original_url_ = url;
// Convert |url| into an appropriate SiteInfo that can be passed to
// SetSiteInfoInternal(). We must do this transformation for any arbitrary
// URL we get from a user, a navigation, or script.
SetSiteInfoInternal(browsing_instance_->GetSiteInfoForURL(
url_info, /* allow_default_instance */ false));
}
void SiteInstanceImpl::SetSite(const SiteInfo& site_info) {
TRACE_EVENT2("navigation", "SiteInstanceImpl::SetSite", "site id",
id_.value(), "siteinfo", site_info.GetDebugString());
DCHECK(!has_site_);
SetSiteInfoInternal(site_info);
}
void SiteInstanceImpl::SetSiteInfoToDefault(
const StoragePartitionConfig& storage_partition_config) {
TRACE_EVENT1("navigation", "SiteInstanceImpl::SetSiteInfoToDefault",
"site id", id_.value());
DCHECK(!has_site_);
default_site_instance_state_ = std::make_unique<DefaultSiteInstanceState>();
original_url_ = GetDefaultSiteURL();
SetSiteInfoInternal(SiteInfo::CreateForDefaultSiteInstance(
GetIsolationContext(), storage_partition_config,
GetWebExposedIsolationInfo()));
}
void SiteInstanceImpl::SetSiteInfoInternal(const SiteInfo& site_info) {
// TODO(acolwell): Add logic to validate |site_url| and |lock_url| are valid.
DCHECK(!has_site_);
CHECK_EQ(site_info.web_exposed_isolation_info(),
browsing_instance_->web_exposed_isolation_info());
if (verify_storage_partition_info_) {
auto old_partition_config = site_info_.storage_partition_config();
auto new_partition_config = site_info.storage_partition_config();
CHECK_EQ(old_partition_config, new_partition_config);
}
// Remember that this SiteInstance has been used to load a URL, even if the
// URL is invalid.
has_site_ = true;
site_info_ = site_info;
// Now that we have a site, register it with the BrowsingInstance. This
// ensures that we won't create another SiteInstance for this site within
// the same BrowsingInstance, because all same-site pages within a
// BrowsingInstance can script each other.
browsing_instance_->RegisterSiteInstance(this);
if (site_info_.requires_origin_keyed_process() &&
!site_info_.requires_origin_keyed_process_by_default()) {
// Track this origin's isolation in the current BrowsingInstance, if it has
// received an origin-keyed process due to an explicit opt-in. This is
// needed to consistently isolate future navigations to this origin in this
// BrowsingInstance, even if its opt-in status changes later.
ChildProcessSecurityPolicyImpl* policy =
ChildProcessSecurityPolicyImpl::GetInstance();
url::Origin origin(url::Origin::Create(site_info_.process_lock_url()));
// This is one of two places that origins can be marked as opted-in, the
// other is
// NavigationRequest::AddSameProcessOriginAgentClusterStateIfNecessary().
// This site handles the case where OAC isolation gets a separate process.
// In future, when SiteInstance Groups are complete, this may revert to
// being the only call site.
policy->AddOriginIsolationStateForBrowsingInstance(
browsing_instance_->isolation_context(), origin,
true /* is_origin_agent_cluster */,
true /* requires_origin_keyed_process */);
}
if (site_info_.does_site_request_dedicated_process_for_coop()) {
// If there was a request to process-isolate `site_info_` from COOP
// headers, notify ChildProcessSecurityPolicy about the new isolated origin
// in the current BrowsingInstance. Note that we must convert the origin
// to a site to avoid breaking document.domain. Typically, the process
// lock URL would already correspond to a site (since we isolate sites, not
// origins, by default), but this isn't always the case. For example, this
// SiteInstance could be isolated with the origin granularity due to
// Origin-Agent-Cluster (see site_info_.requires_origin_keyed_process()
// above).
url::Origin origin(url::Origin::Create(site_info_.process_lock_url()));
GURL site(SiteInfo::GetSiteForOrigin(origin));
ChildProcessSecurityPolicyImpl* policy =
ChildProcessSecurityPolicyImpl::GetInstance();
policy->AddCoopIsolatedOriginForBrowsingInstance(
browsing_instance_->isolation_context(), url::Origin::Create(site),
ChildProcessSecurityPolicy::IsolatedOriginSource::WEB_TRIGGERED);
}
// Update the process reuse policy based on the site.
bool should_use_process_per_site = ShouldUseProcessPerSite();
if (should_use_process_per_site)
process_reuse_policy_ = ProcessReusePolicy::PROCESS_PER_SITE;
if (has_group()) {
LockProcessIfNeeded();
// Inform the embedder if the SiteInstance now has both the process and the
// site assigned. Note that this can be called either here or when setting
// the process in SetProcessInternal() above.
GetContentClient()->browser()->SiteInstanceGotProcessAndSite(this);
// Ensure the process is registered for this site if necessary.
if (should_use_process_per_site) {
RenderProcessHostImpl::RegisterSoleProcessHostForSite(
site_instance_group_->process(), this);
}
}
}
void SiteInstanceImpl::ConvertToDefaultOrSetSite(const UrlInfo& url_info) {
DCHECK(!has_site_);
if (!ShouldUseDefaultSiteInstanceGroup() &&
!browsing_instance_->has_default_site_instance()) {
// We want to set a SiteInfo in this SiteInstance, from information in a
// UrlInfo. The WebExposedIsolationInfo must be compatible for this
// function to not violate WebExposedIsolationInfo isolation invariant
// within a BrowsingInstance.
DCHECK(WebExposedIsolationInfo::AreCompatible(
url_info.web_exposed_isolation_info, GetWebExposedIsolationInfo()));
// If |url_info| has a null WebExposedIsolationInfo, it is compatible with
// any isolation state. We reuse the isolation state of the browsing
// instance for the SiteInfo, to preserve the invariant.
UrlInfo updated_url_info = url_info;
updated_url_info.web_exposed_isolation_info = GetWebExposedIsolationInfo();
const SiteInfo site_info =
SiteInfo::Create(GetIsolationContext(), updated_url_info);
if (CanBePlacedInDefaultSiteInstanceOrGroup(
GetIsolationContext(), updated_url_info.url, site_info)) {
SetSiteInfoToDefault(site_info.storage_partition_config());
AddSiteInfoToDefault(site_info);
DCHECK(browsing_instance_->has_default_site_instance());
return;
}
}
SetSite(url_info);
// If `this` should go in the default SiteInstanceGroup, it needs to be a
// regular SiteInstance with a site (unlike the default SiteInstance), so
// SetSite needs to be called first.
if (ShouldUseDefaultSiteInstanceGroup()) {
MaybeSetDefaultSiteInstanceGroup();
}
}
void SiteInstanceImpl::MaybeSetDefaultSiteInstanceGroup() {
CHECK(ShouldUseDefaultSiteInstanceGroup());
if (!browsing_instance_->has_default_site_instance_group() &&
CanBePlacedInDefaultSiteInstanceOrGroup(GetIsolationContext(),
GetSiteURL(), site_info_)) {
CHECK(HasProcess());
CHECK(has_group());
browsing_instance_->set_default_site_instance_group(
site_instance_group_->GetWeakPtr());
}
}
bool SiteInstanceImpl::CanPutSiteInstanceInDefaultGroup() {
return ShouldUseDefaultSiteInstanceGroup() &&
CanBePlacedInDefaultSiteInstanceOrGroup(GetIsolationContext(),
GetSiteURL(), site_info_);
}
SiteInstanceProcessAssignment
SiteInstanceImpl::GetLastProcessAssignmentOutcome() {
return process_assignment_;
}
const GURL& SiteInstanceImpl::GetSiteURL() {
return site_info_.site_url();
}
const SiteInfo& SiteInstanceImpl::GetSiteInfo() {
return site_info_;
}
SiteInfo SiteInstanceImpl::DeriveSiteInfo(
const UrlInfo& url_info,
bool is_related,
bool disregard_web_exposed_isolation_info) {
if (is_related) {
return browsing_instance_->GetSiteInfoForURL(
url_info, /* allow_default_instance */ true);
}
// If we care about WebExposedIsolationInfo, verify that the passed in
// WebExposedIsolationInfo is compatible with the internal state. If they
// don't, the semantics of the function would be unclear.
if (!disregard_web_exposed_isolation_info) {
DCHECK(WebExposedIsolationInfo::AreCompatible(
url_info.web_exposed_isolation_info, GetWebExposedIsolationInfo()));
}
// At this stage, we either have two values of WebExposedIsolationInfo that
// can be resolved into one, for example when UrlInfo has an empty
// WebExposedIsolationInfo and it is matchable with any isolation state. Or we
// are trying to compute other state, regardless of what the passed in
// WebExposedIsolationInfos are. In both cases, we simply use the
// SiteInstance's value.
UrlInfo overridden_url_info = url_info;
overridden_url_info.web_exposed_isolation_info = GetWebExposedIsolationInfo();
// Keep the same StoragePartition when the storage partition is fixed (e.g.
// for <webview>).
if (IsFixedStoragePartition()) {
overridden_url_info.storage_partition_config =
GetSiteInfo().storage_partition_config();
}
return SiteInfo::Create(GetIsolationContext(), overridden_url_info);
}
bool SiteInstanceImpl::HasSite() const {
return has_site_;
}
bool SiteInstanceImpl::HasRelatedSiteInstance(const SiteInfo& site_info) {
return browsing_instance_->HasSiteInstance(site_info);
}
scoped_refptr<SiteInstance> SiteInstanceImpl::GetRelatedSiteInstance(
const GURL& url) {
return GetRelatedSiteInstanceImpl(UrlInfo(UrlInfoInit(url)));
}
scoped_refptr<SiteInstanceImpl> SiteInstanceImpl::GetRelatedSiteInstanceImpl(
const UrlInfo& url_info) {
return browsing_instance_->GetSiteInstanceForURL(
url_info, /* allow_default_instance */ true);
}
scoped_refptr<SiteInstanceImpl>
SiteInstanceImpl::GetMaybeGroupRelatedSiteInstanceImpl(
const UrlInfo& url_info) {
// There has to be an existing SiteInstanceGroup in order to get one in the
// same group.
CHECK(site_instance_group_);
return browsing_instance_->GetMaybeGroupRelatedSiteInstanceForURL(
url_info, site_instance_group_.get());
}
AgentSchedulingGroupHost& SiteInstanceImpl::GetOrCreateAgentSchedulingGroup() {
// Currently GetOrCreateAgentSchedulingGroup is called in the following
// cases:
// * From the RFH constructor created by CreateSpeculativeRenderFrameHost,
// the function will explicitly create the process for the site instance
// before constructing the RFH.
// * From the RFH constructor created by InitRoot, the function will
// explicitly create the process for the site instance before
// constructing the RFH.
// * From the RFH constructor created by InitChild, the child RFH is assumed
// to share the same process as the parent so the process will already be
// present.
// * In SharedStorageRenderThreadWorkletDriver::StartWorkletService, the
// constructor of SharedStorageRenderThreadWorkletDriver will create
// the process for the site instance.
// Since this is called when SiteInstance already has a process in all these
// cases, and since site_instance_group_ is created when the SiteInstance's
// process is set, there should be no case here when there is no
// site_instance_group_, and no need to call GetOrCreateProcess().
//
// TODO(crbug.com/388998723): Remove the call to GetProcess() after
// verifying there is no DumpWithoutCrashing reports.
if (!site_instance_group_) {
GetProcess();
}
return site_instance_group_->agent_scheduling_group();
}
void SiteInstanceImpl::SetSiteInstanceGroup(SiteInstanceGroup* group) {
// At this point, `this` should not belong to a group. If `this` is being
// created, then there should be no group set. If the group is being set
// because the SiteInstance is getting a new process, the old one should have
// been cleared.
CHECK(!site_instance_group_);
site_instance_group_ = group;
}
void SiteInstanceImpl::ResetSiteInstanceGroup() {
site_instance_group_.reset();
}
bool SiteInstanceImpl::IsRelatedSiteInstance(const SiteInstance* instance) {
return browsing_instance_.get() ==
static_cast<const SiteInstanceImpl*>(instance)
->browsing_instance_.get();
}
size_t SiteInstanceImpl::GetRelatedActiveContentsCount() {
return browsing_instance_->active_contents_count();
}
namespace {
bool SandboxConfigurationsMatch(const SiteInfo& site_info,
const UrlInfo& url_info) {
return site_info.is_sandboxed() == url_info.is_sandboxed &&
site_info.unique_sandbox_id() == url_info.unique_sandbox_id;
}
} // namespace
bool SiteInstanceImpl::IsSuitableForUrlInfo(const UrlInfo& url_info) {
TRACE_EVENT2("navigation", "SiteInstanceImpl::IsSuitableForUrlInfo",
ChromeTrackEvent::kSiteInstance, this, "url_info", url_info);
const GURL& url = url_info.url;
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// If the URL to navigate to can be associated with any site instance,
// we want to keep it in the same process.
if (blink::IsRendererDebugURL(url))
return true;
// Any process can host an about:blank URL, except the one used for error
// pages, which should not commit successful navigations. This check avoids a
// process transfer for browser-initiated navigations to about:blank in a
// dedicated process; without it, IsSuitableHost would consider this process
// unsuitable for about:blank when it compares process locks.
// Renderer-initiated navigations will handle about:blank navigations
// elsewhere and leave them in the source SiteInstance, along with
// about:srcdoc and data:.
if (url.IsAboutBlank() && !site_info_.is_error_page())
return true;
// The is_sandboxed flags and unique_sandbox_ids must match for this to be a
// suitable SiteInstance.
if (!SandboxConfigurationsMatch(GetSiteInfo(), url_info))
return false;
// If the site URL is an extension (e.g., for hosted apps or WebUI) but the
// process is not (or vice versa), make sure we notice and fix it.
// Note: This call must return information that is identical to what
// would be reported in the SiteInstance returned by
// GetRelatedSiteInstance(url).
SiteInfo site_info = DeriveSiteInfo(url_info, /* is_related= */ true);
// If this is a default SiteInstance and the BrowsingInstance gives us a
// non-default SiteInfo even when we explicitly allow the default SiteInstance
// to be considered, then |url| does not belong in the same process as this
// SiteInstance.
if (IsDefaultSiteInstance() && site_info != site_info_)
return false;
// Note that HasProcess() may return true if site_instance_group_->process_ is
// null, in process-per-site cases where there's an existing process
// available. We want to use such a process in the IsSuitableHost check, so we
// may end up assigning process_ in the GetProcess() call below.
if (!HasProcess()) {
// If there is no process or site, then this is a new SiteInstance that can
// be used for anything.
if (!HasSite())
return true;
// If there is no process but there is a site, then the process must have
// been discarded after we navigated away. If the SiteInfos match, then it
// is safe to use this SiteInstance unless it is a guest. Guests are a
// special case because we need to be consistent with the HasProcess() path
// and the IsSuitableHost() call below always returns false for guests.
if (site_info_ == site_info)
return !IsGuest();
// If the site URLs do not match, but neither this SiteInstance nor the
// destination site_url require dedicated processes, then it is safe to use
// this SiteInstance.
if (!RequiresDedicatedProcess() &&
!site_info.RequiresDedicatedProcess(GetIsolationContext())) {
return true;
}
// Otherwise, there's no process, the SiteInfos don't match, and at least
// one of them requires a dedicated process, so it is not safe to use this
// SiteInstance.
return false;
}
return RenderProcessHostImpl::IsSuitableHost(
GetProcess(), GetIsolationContext(), site_info);
}
bool SiteInstanceImpl::RequiresDedicatedProcess() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!has_site_)
return false;
return site_info_.RequiresDedicatedProcess(GetIsolationContext());
}
bool SiteInstanceImpl::RequiresOriginKeyedProcess() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!has_site_)
return false;
// TODO(wjmaclean): once SiteInstanceGroups are ready we may give logically
// (same-process) isolated origins their own SiteInstances ... in that case we
// should consider updating this function.
return site_info_.requires_origin_keyed_process();
}
bool SiteInstanceImpl::IsSandboxed() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!has_site_) {
return false;
}
return site_info_.is_sandboxed();
}
void SiteInstanceImpl::IncrementRelatedActiveContentsCount() {
browsing_instance_->IncrementActiveContentsCount();
}
void SiteInstanceImpl::DecrementRelatedActiveContentsCount() {
browsing_instance_->DecrementActiveContentsCount();
}
BrowserContext* SiteInstanceImpl::GetBrowserContext() {
return browsing_instance_->GetBrowserContext();
}
// static
scoped_refptr<SiteInstance> SiteInstance::Create(
BrowserContext* browser_context) {
DCHECK(browser_context);
return SiteInstanceImpl::Create(browser_context);
}
// static
scoped_refptr<SiteInstance> SiteInstance::CreateForURL(
BrowserContext* browser_context,
const GURL& url) {
return SiteInstanceImpl::CreateForURL(browser_context, url);
}
// static
scoped_refptr<SiteInstanceImpl> SiteInstanceImpl::CreateForURL(
BrowserContext* browser_context,
const GURL& url) {
DCHECK(browser_context);
return SiteInstanceImpl::CreateForUrlInfo(
browser_context, UrlInfo(UrlInfoInit(url)), /*is_guest=*/false,
/*is_fenced=*/false, /*is_fixed_storage_partition=*/false);
}
// static
scoped_refptr<SiteInstance> SiteInstance::CreateForGuest(
BrowserContext* browser_context,
const StoragePartitionConfig& partition_config) {
DCHECK(browser_context);
return SiteInstanceImpl::CreateForGuest(browser_context, partition_config);
}
// static
scoped_refptr<SiteInstance> SiteInstance::CreateForFixedStoragePartition(
BrowserContext* browser_context,
const GURL& url,
const StoragePartitionConfig& partition_config) {
CHECK(browser_context);
return SiteInstanceImpl::CreateForFixedStoragePartition(browser_context, url,
partition_config);
}
// static
bool SiteInstance::ShouldAssignSiteForURL(const GURL& url) {
return SiteInstanceImpl::ShouldAssignSiteForUrlInfo(
UrlInfo(UrlInfoInit(url)));
}
bool SiteInstanceImpl::IsSameSiteWithURL(const GURL& url) {
return IsSameSiteWithURLInfo(UrlInfo(UrlInfoInit(url)));
}
bool SiteInstanceImpl::IsSameSiteWithURLInfo(const UrlInfo& url_info) {
const GURL& url = url_info.url;
if (IsDefaultSiteInstance()) {
// about:blank URLs should always be considered same site just like they are
// in IsSameSite().
if (url.IsAboutBlank())
return true;
// Consider |url| the same site if it could be handled by the
// default SiteInstance and we don't already have a SiteInstance for
// this URL.
// TODO(acolwell): Remove HasSiteInstance() call once we have a way to
// prevent SiteInstances with no site URL from being used for URLs
// that should be routed to the default SiteInstance.
DCHECK_EQ(site_info_.site_url(), GetDefaultSiteURL());
// We're only interested in knowning if we're same-site.
// WebExposedIsolationInfo should not come into play here so we make them
// match explicitly.
UrlInfo updated_url_info = url_info;
updated_url_info.web_exposed_isolation_info = GetWebExposedIsolationInfo();
auto site_info = SiteInfo::Create(GetIsolationContext(), updated_url_info);
return CanBePlacedInDefaultSiteInstanceOrGroup(GetIsolationContext(), url,
site_info) &&
!browsing_instance_->HasSiteInstance(site_info);
}
return SiteInstanceImpl::IsSameSite(
GetIsolationContext(), UrlInfo(UrlInfoInit(site_info_.site_url())),
url_info, true /* should_compare_effective_urls */);
}
bool SiteInstanceImpl::IsGuest() {
return site_info_.is_guest();
}
bool SiteInstanceImpl::IsFixedStoragePartition() {
bool is_fixed_storage_partition =
browsing_instance_->is_fixed_storage_partition();
if (IsGuest()) {
CHECK(is_fixed_storage_partition);
}
return is_fixed_storage_partition;
}
bool SiteInstanceImpl::IsJitDisabled() {
return site_info_.is_jit_disabled();
}
bool SiteInstanceImpl::AreV8OptimizationsDisabled() {
return site_info_.are_v8_optimizations_disabled();
}
bool SiteInstanceImpl::IsPdf() {
return site_info_.is_pdf();
}
const StoragePartitionConfig& SiteInstanceImpl::GetStoragePartitionConfig() {
if (!has_site_) {
// Note: `site_info_` has not been set yet. This is ok as long as the
// StoragePartition of this SiteInstance does not change when `site_info_`
// is actually set. Enable the verification code in SetSiteInfoInternal()
// to verify that the storage partition info does not change.
verify_storage_partition_info_ = true;
}
return site_info_.storage_partition_config();
}
std::string SiteInstanceImpl::GetPartitionDomain(
StoragePartitionImpl* storage_partition) {
auto storage_partition_config = GetStoragePartitionConfig();
// The DCHECK here is to allow the trybots to detect any attempt to introduce
// new code that violates this assumption.
DCHECK_EQ(storage_partition->GetPartitionDomain(),
storage_partition_config.partition_domain());
if (storage_partition->GetPartitionDomain() !=
storage_partition_config.partition_domain()) {
// Trigger crash logging if we encounter a case that violates our
// assumptions.
SCOPED_CRASH_KEY_STRING256("GetPartitionDomain", "domain",
storage_partition->GetPartitionDomain());
SCOPED_CRASH_KEY_STRING256("GetPartitionDomain", "config_domain_key",
storage_partition_config.partition_domain());
base::debug::DumpWithoutCrashing();
// Return the value from the config to preserve legacy behavior until we
// can land a fix.
return storage_partition_config.partition_domain();
}
return storage_partition->GetPartitionDomain();
}
bool SiteInstanceImpl::IsOriginalUrlSameSite(
const UrlInfo& dest_url_info,
bool should_compare_effective_urls) {
if (IsDefaultSiteInstance())
return IsSameSiteWithURLInfo(dest_url_info);
// Here we use an |origin_isolation_request| of kNone (done implicitly in the
// UrlInfoInit constructor) when converting |original_url_| to UrlInfo, since
// (i) the isolation status of this SiteInstance was determined at the time
// |original_url_| was set, and in this case it is |dest_url_info| that is
// currently navigating, and that's where the current isolation request (if
// any) is stored. Whether or not this SiteInstance has origin isolation is a
// separate question, and not what the UrlInfo for |original_url_| is supposed
// to reflect.
return IsSameSite(GetIsolationContext(), UrlInfo(UrlInfoInit(original_url_)),
dest_url_info, should_compare_effective_urls);
}
bool SiteInstanceImpl::IsNavigationSameSite(
const GURL& last_successful_url,
const url::Origin& last_committed_origin,
bool for_outermost_main_frame,
const UrlInfo& dest_url_info) {
// The is_sandboxed flags and unique_sandbox_ids must match for this to be a
// same-site navigation.
if (!SandboxConfigurationsMatch(GetSiteInfo(), dest_url_info))
return false;
// Similarly, do not consider PDF and non-PDF documents to be same-site; they
// should never share a SiteInstance. See https://crbug.com/359345045.
if (IsPdf() != dest_url_info.is_pdf) {
return false;
}
const GURL& dest_url = dest_url_info.url;
BrowserContext* browser_context = GetBrowserContext();
bool should_compare_effective_urls = ShouldCompareEffectiveURLs(
browser_context, this, for_outermost_main_frame, dest_url);
// If IsSuitableForUrlInfo finds a process type mismatch, return false
// even if |dest_url| is same-site. (The URL may have been installed as an
// app since the last time we visited it.)
//
// This check must be skipped for certain same-site navigations from a hosted
// app to non-hosted app, and vice versa, to keep them in the same process
// due to scripting requirements. Otherwise, this would return false due to
// a process privilege level mismatch.
//
// TODO(alexmos): Skipping this check is dangerous, since other bits in
// SiteInfo may disqualify the navigation from being same-site, even when a
// hosted app URL embeds a non-hosted-app same-site URL. Two of these cases,
// sandboxed frames and PDF, are currently handled explicitly above, and a
// couple more are handled in the callers of this function, but this should be
// refactored to more systematically check everything else in SiteInfo. See
// https://crbug.com/349777779.
bool should_check_for_wrong_process =
!IsNavigationAllowedToStayInSameProcessDueToEffectiveURLs(
browser_context, for_outermost_main_frame, dest_url);
if (should_check_for_wrong_process && !IsSuitableForUrlInfo(dest_url_info))
return false;
// In the common case, we use the last successful URL. Thus, we compare
// against the last successful commit when deciding whether to swap this time.
// We convert |last_successful_url| to UrlInfo with |origin_isolation_request|
// set to kNone (done implicitly in the UrlInfoInit constructor) since it
// isn't currently navigating.
if (IsSameSite(GetIsolationContext(),
UrlInfo(UrlInfoInit(last_successful_url)), dest_url_info,
should_compare_effective_urls)) {
return true;
}
// It is possible that last_successful_url was a nonstandard scheme (for
// example, "about:blank"). If so, examine the last committed origin to
// determine the site.
// Similar to above, convert |last_committed_origin| to UrlInfo with
// |origin_isolation_request| set to kNone: this is done implicitly in the
// UrlInfoInit constructor.
if (!last_committed_origin.opaque() &&
IsSameSite(GetIsolationContext(),
UrlInfo(UrlInfoInit(GURL(last_committed_origin.Serialize()))),
dest_url_info, should_compare_effective_urls)) {
return true;
}
// If the last successful URL was "about:blank" with a unique origin (which
// implies that it was a browser-initiated navigation to "about:blank"), none
// of the cases above apply, but we should still allow a scenario like
// foo.com -> about:blank -> foo.com to be treated as same-site, as some
// tests rely on that behavior. To accomplish this, compare |dest_url|
// against the site URL.
if (last_successful_url.IsAboutBlank() && last_committed_origin.opaque() &&
IsOriginalUrlSameSite(dest_url_info, should_compare_effective_urls)) {
return true;
}
// Not same-site.
return false;
}
bool SiteInstanceImpl::IsNavigationAllowedToStayInSameProcessDueToEffectiveURLs(
BrowserContext* browser_context,
bool for_outermost_main_frame,
const GURL& dest_url) {
if (ShouldCompareEffectiveURLs(browser_context, this,
for_outermost_main_frame, dest_url)) {
return false;
}
bool src_has_effective_url = !IsDefaultSiteInstance() &&
HasEffectiveURL(browser_context, original_url());
if (src_has_effective_url)
return true;
return HasEffectiveURL(browser_context, dest_url);
}
// static
bool SiteInstanceImpl::IsSameSite(const IsolationContext& isolation_context,
const UrlInfo& real_src_url_info,
const UrlInfo& real_dest_url_info,
bool should_compare_effective_urls) {
const GURL& real_src_url = real_src_url_info.url;
const GURL& real_dest_url = real_dest_url_info.url;
DCHECK_CURRENTLY_ON(BrowserThread::UI);
BrowserContext* browser_context =
isolation_context.browser_or_resource_context().ToBrowserContext();
DCHECK(browser_context);
DCHECK_NE(real_src_url, GetDefaultSiteURL());
GURL src_url =
should_compare_effective_urls
? SiteInstanceImpl::GetEffectiveURL(browser_context, real_src_url)
: real_src_url;
GURL dest_url =
should_compare_effective_urls
? SiteInstanceImpl::GetEffectiveURL(browser_context, real_dest_url)
: real_dest_url;
// We infer web site boundaries based on the registered domain name of the
// top-level page and the scheme. We do not pay attention to the port if
// one is present, because pages served from different ports can still
// access each other if they change their document.domain variable.
// Some special URLs will match the site instance of any other URL. This is
// done before checking both of them for validity, since we want these URLs
// to have the same site instance as even an invalid one.
if (blink::IsRendererDebugURL(src_url) || blink::IsRendererDebugURL(dest_url))
return true;
// If either URL is invalid, they aren't part of the same site.
if (!src_url.is_valid() || !dest_url.is_valid())
return false;
// To be same-site they must have the same `is_sandbox` flag.
if (real_src_url_info.is_sandboxed != real_dest_url_info.is_sandboxed)
return false;
// If the destination url is just a blank page, we treat them as part of the
// same site.
if (dest_url.IsAboutBlank()) {
// TODO(crbug.com/40266169): It's actually possible for the
// about:blank page to inherit an origin that doesn't match `src_origin`. In
// that case we shouldn't treat it as same-site. Consider changing this
// behavior if all tests can pass.
return true;
}
// If the source and destination URLs are equal excluding the hash, they have
// the same site. This matters for file URLs, where SameDomainOrHost() would
// otherwise return false below.
if (src_url.EqualsIgnoringRef(dest_url))
return true;
url::Origin src_origin = url::Origin::Create(src_url);
url::Origin dest_origin = url::Origin::Create(dest_url);
// If the schemes differ, they aren't part of the same site.
if (src_origin.scheme() != dest_origin.scheme())
return false;
// Rely on an origin comparison if StrictOriginIsolation is enabled for all
// URLs, or if we're comparing against a sandboxed iframe in a per-origin
// mode. Due to an earlier check, at this point
// `real_src_url_info.is_sandboxed` and `real_dest_url_info.is_sandboxed` are
// known to have the same value.
if (SiteIsolationPolicy::IsStrictOriginIsolationEnabled() ||
(real_src_url_info.is_sandboxed &&
blink::features::kIsolateSandboxedIframesGroupingParam.Get() ==
blink::features::IsolateSandboxedIframesGrouping::kPerOrigin)) {
return src_origin == dest_origin;
}
if (!net::registry_controlled_domains::SameDomainOrHost(
src_origin, dest_origin,
net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
return false;
}
// If the sites are the same, check isolated origins. If either URL matches
// an isolated origin, compare origins rather than sites. As an optimization
// to avoid unneeded isolated origin lookups, shortcut this check if the two
// origins are the same.
if (src_origin == dest_origin)
return true;
auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
url::Origin src_isolated_origin;
url::Origin dest_isolated_origin;
bool src_origin_is_isolated = policy->GetMatchingProcessIsolatedOrigin(
isolation_context, src_origin,
real_src_url_info.RequestsOriginKeyedProcess(isolation_context),
&src_isolated_origin);
bool dest_origin_is_isolated = policy->GetMatchingProcessIsolatedOrigin(
isolation_context, dest_origin,
real_dest_url_info.RequestsOriginKeyedProcess(isolation_context),
&dest_isolated_origin);
if (src_origin_is_isolated || dest_origin_is_isolated) {
// Compare most specific matching origins to ensure that a subdomain of an
// isolated origin (e.g., https://subdomain.isolated.foo.com) also matches
// the isolated origin's site URL (e.g., https://isolated.foo.com).
return src_isolated_origin == dest_isolated_origin;
}
return true;
}
bool SiteInstanceImpl::DoesSiteInfoForURLMatch(const UrlInfo& url_info) {
// We want to compare this SiteInstance's SiteInfo to the SiteInfo that would
// be generated by the passed in UrlInfo. For them to match, the
// WebExposedIsolationInfo must be compatible.
if (!WebExposedIsolationInfo::AreCompatible(
url_info.web_exposed_isolation_info, GetWebExposedIsolationInfo())) {
return false;
}
// Similarly, the CrossOriginIsolationKeys should match.
if (GetSiteInfo().agent_cluster_key() &&
GetSiteInfo().agent_cluster_key()->GetCrossOriginIsolationKey() !=
url_info.cross_origin_isolation_key) {
return false;
}
// If the passed in UrlInfo has a null WebExposedIsolationInfo, meaning that
// it is compatible with any isolation state, we reuse the isolation state of
// this SiteInstance's SiteInfo so the member comparison of SiteInfos will
// match.
UrlInfo updated_url_info = url_info;
updated_url_info.web_exposed_isolation_info =
site_info_.web_exposed_isolation_info();
auto site_info = SiteInfo::Create(GetIsolationContext(), updated_url_info);
if (!ShouldUseDefaultSiteInstanceGroup() &&
kCreateForURLAllowsDefaultSiteInstance &&
CanBePlacedInDefaultSiteInstanceOrGroup(GetIsolationContext(),
url_info.url, site_info)) {
site_info = SiteInfo::CreateForDefaultSiteInstance(
GetIsolationContext(), site_info.storage_partition_config(),
GetWebExposedIsolationInfo());
}
return site_info_.IsExactMatch(site_info);
}
void SiteInstanceImpl::RegisterAsDefaultOriginIsolation(
const url::Origin& previously_visited_origin) {
auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
policy->AddDefaultIsolatedOriginIfNeeded(
GetIsolationContext(), previously_visited_origin,
true /* is_global_walk_or_frame_removal */);
}
// static
bool SiteInstanceImpl::CanBePlacedInDefaultSiteInstanceOrGroup(
const IsolationContext& isolation_context,
const GURL& url,
const SiteInfo& site_info) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Empty URLs, like the initial empty document, should not be placed in the
// default SiteInstance or group. The initial empty document's SiteInstance
// can be reused, including for navigations to isolated sites. Avoid the case
// where a SiteInstance or group set as the default can then become isolated.
if (url.is_empty()) {
return false;
}
// Exclude "file://" URLs from the default SiteInstance to prevent the
// default SiteInstance/Group process from accumulating file access grants
// that could be exploited by other non-isolated sites.
if (url.SchemeIs(url::kFileScheme))
return false;
// Don't use the default SiteInstance/Group when SiteInstance doesn't assign a
// site URL for |url|, since in that case the SiteInstance should remain
// unused, and a subsequent navigation should always be able to reuse it,
// whether or not it's to a site requiring a dedicated process or to a site
// that will use the default SiteInstance/Group.
if (!ShouldAssignSiteForURL(url))
return false;
// Allow the default SiteInstance to be used for sites that don't need to be
// isolated in their own process.
return !site_info.RequiresDedicatedProcess(isolation_context);
}
// static
GURL SiteInstanceImpl::GetEffectiveURL(BrowserContext* browser_context,
const GURL& url) {
DCHECK(browser_context);
return GetContentClient()->browser()->GetEffectiveURL(browser_context, url);
}
// static
bool SiteInstanceImpl::HasEffectiveURL(BrowserContext* browser_context,
const GURL& url) {
return GetEffectiveURL(browser_context, url) != url;
}
void SiteInstanceImpl::LockProcessIfNeeded() {
TRACE_EVENT("navigation", "SiteInstanceImpl::LockProcessIfNeeded",
ChromeTrackEvent::kSiteInstance, this);
RenderProcessHost* process = site_instance_group_->process();
ChildProcessSecurityPolicyImpl* policy =
ChildProcessSecurityPolicyImpl::GetInstance();
ProcessLock process_lock = process->GetProcessLock();
StoragePartitionImpl* storage_partition =
static_cast<StoragePartitionImpl*>(process->GetStoragePartition());
if (!has_site_) {
CHECK(!process_lock.is_locked_to_site())
<< "A process that's already locked to " << process_lock.ToString()
<< " cannot be updated to a more permissive lock";
// Update the process lock state to signal that the process has been
// associated with a SiteInstance that is not locked to a site yet. Note
// that even if the process lock is already set to a lock that allows any
// site, we still need to notify ChildProcessSecurityPolicy about the
// current SiteInstance's IsolationContext, so that the corresponding
// BrowsingInstance can be associated with |process_|. See
// https://crbug.com/1135539.
if (process_lock.is_invalid()) {
auto new_process_lock = ProcessLock::CreateAllowAnySite(
storage_partition->GetConfig(), GetWebExposedIsolationInfo());
process->SetProcessLock(GetIsolationContext(), new_process_lock);
} else {
CHECK(process_lock.allows_any_site())
<< "Unexpected process lock " << process_lock.ToString();
policy->IncludeIsolationContext(process->GetDeprecatedID(),
GetIsolationContext());
}
return;
}
DCHECK(HasSite());
DCHECK_EQ(storage_partition->GetConfig(),
site_info_.storage_partition_config());
if (site_info_.ShouldLockProcessToSite(GetIsolationContext())) {
ProcessLock lock_to_set = ProcessLock::FromSiteInfo(GetSiteInfo());
if (!process_lock.is_locked_to_site()) {
// TODO(nick): When all sites are isolated, this operation provides
// strong protection. If only some sites are isolated, we need
// additional logic to prevent the non-isolated sites from requesting
// resources for isolated sites. https://crbug.com/509125
TRACE_EVENT_INSTANT(
"navigation",
"SiteInstanceImpl::LockProcessIfNeeded_set_process_lock", "lock",
lock_to_set.ToString());
process->SetProcessLock(GetIsolationContext(), lock_to_set);
} else if (process_lock != lock_to_set) {
// We should never attempt to reassign a different origin lock to a
// process.
base::debug::SetCrashKeyString(bad_message::GetRequestedSiteInfoKey(),
site_info_.GetDebugString());
policy->LogKilledProcessOriginLock(process->GetDeprecatedID());
NOTREACHED() << "Trying to lock a process to " << lock_to_set.ToString()
<< " but the process is already locked to "
<< process_lock.ToString();
} else {
// Process already has the right origin lock assigned. This case will
// happen for commits to |site_info_| after the first one.
}
} else {
if (process_lock.is_locked_to_site()) {
// The site that we're committing doesn't require a dedicated
// process, but it has been put in a process for a site that does.
base::debug::SetCrashKeyString(bad_message::GetRequestedSiteInfoKey(),
site_info_.GetDebugString());
policy->LogKilledProcessOriginLock(process->GetDeprecatedID());
NOTREACHED() << "Trying to commit non-isolated site " << site_info_
<< " in process locked to " << process_lock.ToString();
} else if (process_lock.is_invalid()) {
// Update the process lock state to signal that the process has been
// associated with a SiteInstance that is not locked to a site yet.
auto new_process_lock = ProcessLock::CreateAllowAnySite(
storage_partition->GetConfig(), GetWebExposedIsolationInfo());
process->SetProcessLock(GetIsolationContext(), new_process_lock);
} else {
CHECK(process_lock.allows_any_site())
<< "Unexpected process lock " << process_lock.ToString();
}
}
// From now on, this process should be considered "tainted" for future
// process reuse decisions:
// (1) If |site_info_| required a dedicated process, this SiteInstance's
// process can only host URLs for the same site.
// (2) Even if |site_info_| does not require a dedicated process, this
// SiteInstance's process still cannot be reused to host other sites
// requiring dedicated sites in the future.
// We can get here either when we commit a URL into a SiteInstance that does
// not yet have a site, or when we create a process for a SiteInstance with a
// preassigned site.
process->SetIsUsed();
// Track which isolation contexts use the given process. This lets
// ChildProcessSecurityPolicyImpl (e.g. CanAccessDataForOrigin) determine
// whether a given URL should require a lock or not (a dynamically isolated
// origin may require a lock in some isolation contexts but not in others).
policy->IncludeIsolationContext(process->GetDeprecatedID(),
GetIsolationContext());
}
const WebExposedIsolationInfo& SiteInstanceImpl::GetWebExposedIsolationInfo()
const {
return browsing_instance_->web_exposed_isolation_info();
}
bool SiteInstanceImpl::IsCrossOriginIsolated() const {
return GetWebExposedIsolationInfo().is_isolated() ||
(site_info_.agent_cluster_key() &&
site_info_.agent_cluster_key()->GetCrossOriginIsolationKey() &&
site_info_.agent_cluster_key()
->GetCrossOriginIsolationKey()
->cross_origin_isolation_mode ==
CrossOriginIsolationMode::kConcrete);
}
// static
void SiteInstance::StartIsolatingSite(
BrowserContext* context,
const GURL& url,
ChildProcessSecurityPolicy::IsolatedOriginSource source,
bool should_persist) {
if (!SiteIsolationPolicy::AreDynamicIsolatedOriginsEnabled())
return;
// Ignore attempts to isolate origins that are not supported. Do this here
// instead of relying on AddFutureIsolatedOrigins()'s internal validation, to
// avoid the runtime warning generated by the latter.
url::Origin origin(url::Origin::Create(url));
if (!IsolatedOriginUtil::IsValidIsolatedOrigin(origin))
return;
// Convert |url| to a site, to avoid breaking document.domain. Note that
// this doesn't use effective URL resolution or other special cases from
// GetSiteForURL() and simply converts |origin| to a scheme and eTLD+1.
GURL site(SiteInfo::GetSiteForOrigin(origin));
ChildProcessSecurityPolicyImpl* policy =
ChildProcessSecurityPolicyImpl::GetInstance();
url::Origin site_origin(url::Origin::Create(site));
policy->AddFutureIsolatedOrigins({site_origin}, source, context);
// This function currently assumes the new isolated site should persist
// across restarts, so ask the embedder to save it, excluding off-the-record
// profiles.
if (!context->IsOffTheRecord() && should_persist) {
GetContentClient()->browser()->PersistIsolatedOrigin(context, site_origin,
source);
}
}
void SiteInstanceImpl::WriteIntoTrace(
perfetto::TracedProto<perfetto::protos::pbzero::SiteInstance> proto) {
proto->set_site_instance_id(GetId().value());
proto->set_browsing_instance_id(GetBrowsingInstanceId().value());
proto->set_is_default(IsDefaultSiteInstance());
proto->set_has_process(HasProcess());
proto->set_related_active_contents_count(GetRelatedActiveContentsCount());
proto.Set(TraceProto::kSiteInstanceGroup, group());
if (group()) {
proto->set_active_rfh_count(site_instance_group_->active_frame_count());
}
perfetto::TracedDictionary dict = std::move(proto).AddDebugAnnotations();
dict.Add("site_info", site_info_);
}
int SiteInstanceImpl::EstimateOriginAgentClusterOverheadForMetrics() {
return browsing_instance_->EstimateOriginAgentClusterOverhead();
}
scoped_refptr<SiteInstanceImpl>
SiteInstanceImpl::GetCompatibleSandboxedSiteInstance(
const UrlInfo& url_info,
const url::Origin& parent_origin) {
DCHECK(!IsDefaultSiteInstance());
DCHECK(has_site_);
DCHECK(!GetSiteInfo().is_sandboxed());
DCHECK(url_info.url.IsAboutSrcdoc());
UrlInfo sandboxed_url_info = url_info;
// Since the input `url_info` has a srcdoc url, using the url as-is will
// result in a SiteInfo that's not very specific, so we need something more
// meaningful. Ideally we'd use the UrlInfo used to load the parent, but we
// don't have that anymore, so we use the parent's origin which should be
// close enough. We use GetTupleOrPrecursorTupleIfOpaque in case
// `parent_origin` is opaque.
sandboxed_url_info.url =
parent_origin.GetTupleOrPrecursorTupleIfOpaque().GetURL();
// The `url_info` should already have its is_sandboxed flag set if we're here.
DCHECK(sandboxed_url_info.is_sandboxed);
DCHECK(!sandboxed_url_info.origin);
// At this point assume all other fields in the input `url_info` are correct.
auto sandboxed_site_info =
SiteInfo::Create(GetIsolationContext(), sandboxed_url_info);
auto result =
browsing_instance_->GetSiteInstanceForSiteInfo(sandboxed_site_info);
result->original_url_ = original_url_;
return result;
}
RenderProcessHost* SiteInstanceImpl::GetDefaultProcessForBrowsingInstance() {
if (ShouldUseDefaultSiteInstanceGroup()) {
return browsing_instance_->has_default_site_instance_group()
? browsing_instance_->default_site_instance_group()->process()
: nullptr;
} else {
if (SiteInstanceImpl* default_instance =
browsing_instance_->default_site_instance()) {
return default_instance->HasProcess() ? default_instance->GetProcess()
: nullptr;
}
return nullptr;
}
}
void SiteInstanceImpl::SetProcessForTesting(RenderProcessHost* process) {
SetProcessInternal(process);
}
void SiteInstanceImpl::IncrementActiveDocumentCount(
const SiteInfo& url_derived_site_info) {
if (url_derived_site_info.site_url().is_empty()) {
// This can happen when this function is called when destructing an active
// RenderFrameHost, e.g. on frame detach. In this case, there's no need to
// increment the count.
return;
}
if (active_document_counts_.contains(url_derived_site_info)) {
active_document_counts_[url_derived_site_info]++;
} else {
active_document_counts_[url_derived_site_info] = 1;
}
}
void SiteInstanceImpl::DecrementActiveDocumentCount(
const SiteInfo& url_derived_site_info) {
if (url_derived_site_info.site_url().is_empty()) {
// This can happen when this function is called for the initial
// RenderFrameHost, whose `url_derived_site_info` was never set. In that
// case, `IncrementActiveDocumentCount()` will never be called and the map
// won't contain the SiteInfo, so just return early here.
return;
}
CHECK(active_document_counts_.contains(url_derived_site_info));
active_document_counts_[url_derived_site_info]--;
if (active_document_counts_[url_derived_site_info] == 0) {
active_document_counts_.erase(url_derived_site_info);
}
}
size_t SiteInstanceImpl::GetActiveDocumentCount(
const SiteInfo& url_derived_site_info) {
if (active_document_counts_.contains(url_derived_site_info)) {
return active_document_counts_[url_derived_site_info];
}
return 0;
}
} // namespace content
|