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

content / browser / browsing_instance.h [blame]

// 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.

#ifndef CONTENT_BROWSER_BROWSING_INSTANCE_H_
#define CONTENT_BROWSER_BROWSING_INSTANCE_H_

#include <stddef.h>

#include <optional>

#include "base/check_op.h"
#include "base/gtest_prod_util.h"
#include "base/lazy_instance.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "content/browser/isolation_context.h"
#include "content/browser/security/coop/coop_related_group.h"
#include "content/browser/site_instance_group_manager.h"
#include "content/browser/web_exposed_isolation_info.h"
#include "content/common/content_export.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/render_process_host_observer.h"
#include "content/public/browser/storage_partition_config.h"
#include "url/origin.h"

class GURL;

namespace content {
class SiteInfo;
class SiteInstanceGroup;
class SiteInstanceImpl;
struct UrlInfo;

///////////////////////////////////////////////////////////////////////////////
//
// BrowsingInstance class
//
// A browsing instance corresponds to the notion of a "unit of related browsing
// contexts" in the HTML 5 spec.  Intuitively, it represents a collection of
// tabs and frames that can have script connections to each other.  In that
// sense, it reflects the user interface, and not the contents of the tabs and
// frames.
//
// We further subdivide a BrowsingInstance into SiteInstances, which represent
// the documents within each BrowsingInstance that are from the same site and
// thus can have script access to each other.  Different SiteInstances can
// safely run in different processes, because their documents cannot access
// each other's contents (due to the same origin policy).
//
// It is important to only have one SiteInstance per site within a given
// BrowsingInstance.  This is because any two documents from the same site
// might be able to script each other if they are in the same BrowsingInstance.
// Thus, they must be rendered in the same process.
//
// A BrowsingInstance is live as long as any SiteInstance has a reference to
// it, and thus as long as any SiteInstanceGroup within it exists.  A
// SiteInstance is live as long as any NavigationEntry or RenderFrameHost have
// references to it.  Because both classes are RefCounted, they do not need to
// be manually deleted.
//
// BrowsingInstance has no public members, as it is designed to be
// visible only from the SiteInstance and CoopRelatedGroup classes. To get a new
// SiteInstance that is part of the same BrowsingInstance, use
// SiteInstance::GetRelatedSiteInstance. Because of this, BrowsingInstances and
// SiteInstances are tested together in site_instance_unittest.cc.
//
// Note that a browsing instance in the browser is independently tracked in
// the renderer inside blink::Page::RelatedPages() method (in theory the browser
// and renderer should always stay in sync).
//
///////////////////////////////////////////////////////////////////////////////
class CONTENT_EXPORT BrowsingInstance final
    : public base::RefCounted<BrowsingInstance> {
 public:
  BrowsingInstance(const BrowsingInstance&) = delete;
  BrowsingInstance& operator=(const BrowsingInstance&) = delete;

 private:
  friend class base::RefCounted<BrowsingInstance>;
  friend class SiteInstanceGroup;
  friend class SiteInstanceImpl;
  friend class CoopRelatedGroup;
  FRIEND_TEST_ALL_PREFIXES(SiteInstanceGroupTest, BrowsingInstanceLifetime);
  FRIEND_TEST_ALL_PREFIXES(SiteInstanceTest, OneSiteInstancePerSite);
  FRIEND_TEST_ALL_PREFIXES(SiteInstanceTest,
                           OneSiteInstancePerSiteInBrowserContext);

  // Return an ID of the next BrowsingInstance to be created.  This ID is
  // guaranteed to be higher than any ID of an existing BrowsingInstance.  This
  // does *not* increment the global counter used for assigning
  // BrowsingInstance IDs: that happens only in the BrowsingInstance
  // constructor.
  static BrowsingInstanceId NextBrowsingInstanceId();

  // Create a new BrowsingInstance.
  //
  // `web_exposed_isolation_info` indicates whether the BrowsingInstance
  // should contain only cross-origin isolated pages, i.e. pages with
  // cross-origin-opener-policy set to same-origin and
  // cross-origin-embedder-policy set to require-corp, and if so, from which
  // top level origin.
  //
  // `is_guest` specifies whether this BrowsingInstance will
  // be used in a <webview> guest; `is_fenced` specifies whether this
  // BrowsingInstance is used inside a fenced frame.
  // `is_fixed_storage_partition` indicates whether the current
  // StoragePartition will apply to future navigations. It must be set to true
  // if `is_guest` is true. Note that `is_guest`, `is_fenced`, and
  // `is_fixed_storage_partition` cannot change over the lifetime of the
  // BrowsingInstance.
  //
  // `coop_related_group` represents the CoopRelatedGroup to which this
  // BrowsingInstance belongs. Pages that live in BrowsingInstances in the same
  // group can communicate with each other through a subset of the WindowProxy
  // APIs. This is only used for COOP logic and for all other cases should
  // simply be nullptr. The constructor will take care of building a new group.
  //
  // If `common_coop_origin` is set, it indicates that all documents hosted by
  // the BrowsingInstance have the same COOP value defined by the given origin.
  explicit BrowsingInstance(
      BrowserContext* context,
      const WebExposedIsolationInfo& web_exposed_isolation_info,
      bool is_guest,
      bool is_fenced,
      bool is_fixed_storage_partition,
      const scoped_refptr<CoopRelatedGroup>& coop_related_group,
      std::optional<url::Origin> common_coop_origin);

  ~BrowsingInstance();

  // Get the browser context to which this BrowsingInstance belongs.
  BrowserContext* GetBrowserContext() const;

  // Get the IsolationContext associated with this BrowsingInstance.  This can
  // be used to track this BrowsingInstance in other areas of the code, along
  // with any other state needed to make isolation decisions.
  const IsolationContext& isolation_context() { return isolation_context_; }

  // Return true if the StoragePartition should be preserved across future
  // navigations in the frames belonging to this BrowsingInstance. For <webview>
  // tags, this always returns true.
  bool is_fixed_storage_partition() { return is_fixed_storage_partition_; }

  // Get the SiteInstanceGroupManager that controls all of the SiteInstance
  // groups associated with this BrowsingInstance.
  SiteInstanceGroupManager& site_instance_group_manager() {
    return site_instance_group_manager_;
  }

  // Returns whether this BrowsingInstance has registered a SiteInstance for
  // the site of |site_info|.
  bool HasSiteInstance(const SiteInfo& site_info);

  // Get the SiteInstance responsible for rendering the given UrlInfo.  Should
  // create a new one if necessary, but should not create more than one
  // SiteInstance per site.
  //
  // |allow_default_instance| should be set to true in cases where the caller
  // is ok with |url| sharing a process with other sites that do not require
  // a dedicated process. Note that setting this to true means that the
  // SiteInstanceImpl you get back may return "http://unisolated.invalid" for
  // GetSiteURL() and lock_url() calls because the default instance is not
  // bound to a single site.
  scoped_refptr<SiteInstanceImpl> GetSiteInstanceForURL(
      const UrlInfo& url_info,
      bool allow_default_instance);

  // Searches existing SiteInstances in the BrowsingInstance and returns a
  // pointer to the (unique) SiteInstance that matches `site_info`, if any.
  // If no matching SiteInstance is found, then a new SiteInstance is created
  // in this BrowsingInstance with its site set to `site_info`.
  scoped_refptr<SiteInstanceImpl> GetSiteInstanceForSiteInfo(
      const SiteInfo& site_info);

  // Return a SiteInstance in the same CoopRelatedGroup as this
  // BrowsingInstance. It might or might not be in a new BrowsingInstance, and
  // if it reuses an existing BrowsingInstance of the group, it might reuse an
  // appropriate SiteInstance as well.
  scoped_refptr<SiteInstanceImpl> GetCoopRelatedSiteInstanceForURL(
      const UrlInfo& url_info,
      bool allow_default_instance);

  // Returns a SiteInfo with site and process-lock URLs for |url_info| that are
  // identical with what these values would be if we called
  // GetSiteInstanceForURL() with the same `url_info` and
  // `allow_default_instance`. This method is used when we need this
  // information, but do not want to create a SiteInstance yet.
  //
  // Note: Unlike ComputeSiteInfoForURL() this method can return a SiteInfo for
  // a default SiteInstance, if `url_info` can be placed in the default
  // SiteInstance and `allow_default_instance` is true.
  //
  // Note: Since we're asking to get a SiteInfo that would belong in this
  // BrowsingInstance, it is mandatory that |url_info|'s
  // web_exposed_isolation_info is compatible with the BrowsingInstance's
  // internal WebExposedIsolationInfo value.
  SiteInfo GetSiteInfoForURL(const UrlInfo& url_info,
                             bool allow_default_instance);

  // Helper function used by GetSiteInstanceForURL() and GetSiteInfoForURL()
  // that returns an existing SiteInstance from |site_instance_map_| or
  // returns |default_site_instance_| if |allow_default_instance| is true and
  // other conditions are met. If there is no existing SiteInstance that is
  // appropriate for |url_info|, |allow_default_instance| combination, then a
  // nullptr is returned.
  //
  // Note: This method is not intended to be called by code outside this object.
  scoped_refptr<SiteInstanceImpl> GetSiteInstanceForURLHelper(
      const UrlInfo& url_info,
      bool allow_default_instance);

  // Adds the given SiteInstance to our map, to ensure that we do not create
  // another SiteInstance for the same site.
  void RegisterSiteInstance(SiteInstanceImpl* site_instance);

  // Removes the given SiteInstance from our map, after all references to it
  // have been deleted.  This means it is safe to create a new SiteInstance
  // if the user later visits a page from this site, within this
  // BrowsingInstance.
  void UnregisterSiteInstance(SiteInstanceImpl* site_instance);

  // Returns the token uniquely identifying the CoopRelatedGroup this
  // BrowsingInstance belongs to. This might be used in the renderer, as opposed
  // to IDs.
  base::UnguessableToken coop_related_group_token() const {
    return coop_related_group_->token();
  }

  // Returns the token uniquely identifying this BrowsingInstance. See member
  // declaration for more context.
  base::UnguessableToken token() const { return token_; }

  // Returns the total number of WebContents either living in this
  // BrowsingInstance or that can communicate with it via the CoopRelatedGroup.
  size_t GetCoopRelatedGroupActiveContentsCount();

  // Tracks the number of WebContents currently in this BrowsingInstance.
  // Note: We also separately track the number of WebContents in the entire
  // CoopRelatedGroup, and keep the per-BrowsingInstance counts for validity
  // checks.
  void IncrementActiveContentsCount();
  void DecrementActiveContentsCount();

  bool HasDefaultSiteInstance() const {
    return default_site_instance_ != nullptr;
  }

  // Helper function used by other methods in this class to ensure consistent
  // mapping between |url_info| and SiteInfo. This method will never return a
  // SiteInfo for the default SiteInstance. It will always return something
  // specific to |url_info|.
  //
  // Note: This should not be used by code outside this class.
  SiteInfo ComputeSiteInfoForURL(const UrlInfo& url_info) const;

  // Computes the number of extra SiteInstances for each site due to OAC's
  // splitting a site into isolated origins.
  int EstimateOriginAgentClusterOverhead();

  // Map of SiteInfo to SiteInstance, to ensure we only have one SiteInstance
  // per SiteInfo. See https://crbug.com/1085275#c2 for the rationale behind
  // why SiteInfo is the right class to key this on.
  typedef std::map<SiteInfo, raw_ptr<SiteInstanceImpl, CtnExperimental>>
      SiteInstanceMap;

  // Returns the cross-origin isolation status of the BrowsingInstance.
  const WebExposedIsolationInfo& web_exposed_isolation_info() const {
    return web_exposed_isolation_info_;
  }

  SiteInstanceImpl* default_site_instance() { return default_site_instance_; }

  const std::optional<url::Origin>& common_coop_origin() const {
    return common_coop_origin_;
  }

  // The next available browser-global BrowsingInstance ID.
  static int next_browsing_instance_id_;

  // The IsolationContext associated with this BrowsingInstance.  This will not
  // change after the BrowsingInstance is constructed.
  //
  // This holds a common BrowserContext to which all SiteInstances in this
  // BrowsingInstance must belong.
  const IsolationContext isolation_context_;

  // Manages all SiteInstance groups for this BrowsingInstance.
  SiteInstanceGroupManager site_instance_group_manager_;

  // Map of site to SiteInstance, to ensure we only have one SiteInstance per
  // site.  The site string should be the possibly_invalid_spec() of a GURL
  // obtained with SiteInstanceImpl::GetSiteForURL.  Note that this map may not
  // contain every active SiteInstance, because a race exists where two
  // SiteInstances can be assigned to the same site.  This is ok in rare cases.
  // It also does not contain SiteInstances which have not yet been assigned a
  // site, such as about:blank.  See SiteInstance::ShouldAssignSiteForURL.
  // This map only contains instances that map to a single site. The
  // |default_site_instance_|, which associates multiple sites with a single
  // instance, is not contained in this map.
  SiteInstanceMap site_instance_map_;

  // Number of WebContentses currently using this BrowsingInstance.
  size_t active_contents_count_;

  // SiteInstance to use if a URL does not correspond to an instance in
  // |site_instance_map_| and it does not require a dedicated process.
  // This field and site_instance_group_manager_.default_process_ are mutually
  // exclusive and this field should only be set if
  // kProcessSharingWithStrictSiteInstances is not enabled. This is a raw
  // pointer to avoid a reference cycle between the BrowsingInstance and the
  // SiteInstanceImpl. Note: This can hold cross-origin isolated SiteInstances.
  // It will however only do so under certain specific circumstances (for
  // example on a low memory device), which don't use the COOP isolation
  // heuristic that normally prevents the use of default SiteInstances for
  // cross-origin isolated pages.
  raw_ptr<SiteInstanceImpl> default_site_instance_;

  // The cross-origin isolation status of the BrowsingInstance. This indicates
  // whether this BrowsingInstance is hosting only cross-origin isolated pages
  // and if so, from which top level origin.
  const WebExposedIsolationInfo web_exposed_isolation_info_;

  // The StoragePartitionConfig that must be used by all SiteInstances in this
  // BrowsingInstance. This will be set to the StoragePartitionConfig of the
  // first SiteInstance that has its SiteInfo assigned in this
  // BrowsingInstance, and cannot be changed afterwards.
  //
  // See crbug.com/1212266 for more context on why we track the
  // StoragePartitionConfig here.
  std::optional<StoragePartitionConfig> storage_partition_config_;

  // The CoopRelatedGroup this BrowsingInstance belongs to. BrowsingInstances in
  // the same CoopRelatedGroup have limited window proxy access to each other.
  // In most cases, a CoopRelatedGroup will only contain a single
  // BrowsingInstance, unless pages that use COOP: restrict-properties headers
  // are involved.
  scoped_refptr<CoopRelatedGroup> coop_related_group_;

  // If set, indicates that all documents in this BrowsingInstance share the
  // same COOP value defined by the given origin. In practice, this can only be
  // the case for COOP: same-origin and COOP: restrict-properties.
  //
  // For COOP: same-origin, this will be enforced by COOP swap rules and the
  // value is recorded for invariant checking.
  //
  // For COOP: restrict-properties, this is also used to make sure that the
  // BrowsingInstance is suitable when we're trying to put a new document into
  // an existing BrowsingInstance that is part of the CoopRelatedGroup. To
  // prevent unwanted access, a document with COOP: restrict-properties set from
  // origin a.com should only be put in a BrowsingInstance that holds such
  // documents. This would otherwise break the access guarantees that we have
  // given, of only being able to DOM script same-origin same-COOP documents,
  // and to have limited cross-origin communication with all other pages.
  //
  // TODO(crbug.com/40879437): This assumes that popups opened from
  // cross-origin iframes are opened with no-opener. Once COOP inheritance for
  // those cases is figured out, change the mentions of origin to "COOP origin".
  std::optional<url::Origin> common_coop_origin_;

  // Set to true if the StoragePartition should be preserved across future
  // navigations in the frames belonging to this BrowsingInstance. For <webview>
  // tags, this is always true.
  //
  // TODO(crbug.com/40943418): We actually always want this behavior. Remove
  // this bit when we are ready.
  const bool is_fixed_storage_partition_;

  // A token uniquely identifying this BrowsingInstance. This is used in case we
  // need this information available in the renderer process, rather than
  // sending an ID. Both IDs and Tokens are necessary, because some parts of the
  // process model use the ordering of the IDs, that cannot be provided by
  // tokens alone. Also note that IDs are defined in IsolationContext while
  // tokens are more conveniently defined here.
  const base::UnguessableToken token_ = base::UnguessableToken::Create();
};

}  // namespace content

#endif  // CONTENT_BROWSER_BROWSING_INSTANCE_H_