blob: 0777e2cf2b6c95d110228e845f6715f13d4e7124 [file] [log] [blame]
/*
* Copyright (C) 2009 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h"
#include <memory>
#include <utility>
#include "third_party/blink/renderer/platform/bindings/dom_data_store.h"
#include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
#include "third_party/blink/renderer/platform/wtf/hash_traits.h"
#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
namespace blink {
unsigned DOMWrapperWorld::number_of_non_main_worlds_in_main_thread_ = 0;
// This does not contain the main world because the WorldMap needs
// non-default hashmap traits (WTF::UnsignedWithZeroKeyHashTraits) to contain
// it for the main world's id (0), and it may change the performance trends.
// (see https://crbug.com/704778#c6).
using WorldMap = HashMap<int, DOMWrapperWorld*>;
static WorldMap& GetWorldMap() {
DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<WorldMap>, map, ());
return *map;
}
#if DCHECK_IS_ON()
static bool IsMainWorldId(int world_id) {
return world_id == DOMWrapperWorld::kMainWorldId;
}
#endif
scoped_refptr<DOMWrapperWorld> DOMWrapperWorld::Create(v8::Isolate* isolate,
WorldType world_type) {
DCHECK_NE(WorldType::kIsolated, world_type);
int world_id = GenerateWorldIdForType(world_type);
if (world_id == kInvalidWorldId)
return nullptr;
return base::AdoptRef(new DOMWrapperWorld(isolate, world_type, world_id));
}
DOMWrapperWorld::DOMWrapperWorld(v8::Isolate* isolate,
WorldType world_type,
int world_id)
: world_type_(world_type),
world_id_(world_id),
dom_data_store_(std::make_unique<DOMDataStore>(isolate, IsMainWorld())) {
switch (world_type_) {
case WorldType::kMain:
// The main world is managed separately from worldMap(). See worldMap().
break;
case WorldType::kIsolated:
case WorldType::kInspectorIsolated:
case WorldType::kRegExp:
case WorldType::kTesting:
case WorldType::kForV8ContextSnapshotNonMain:
case WorldType::kWorker: {
WorldMap& map = GetWorldMap();
DCHECK(!map.Contains(world_id_));
map.insert(world_id_, this);
if (IsMainThread())
number_of_non_main_worlds_in_main_thread_++;
break;
}
}
}
DOMWrapperWorld& DOMWrapperWorld::MainWorld() {
DCHECK(IsMainThread());
DEFINE_STATIC_REF(
DOMWrapperWorld, cached_main_world,
(DOMWrapperWorld::Create(v8::Isolate::GetCurrent(), WorldType::kMain)));
return *cached_main_world;
}
void DOMWrapperWorld::AllWorldsInCurrentThread(
Vector<scoped_refptr<DOMWrapperWorld>>& worlds) {
if (IsMainThread())
worlds.push_back(&MainWorld());
for (DOMWrapperWorld* world : GetWorldMap().Values())
worlds.push_back(world);
}
void DOMWrapperWorld::Trace(const ScriptWrappable* script_wrappable,
Visitor* visitor) {
// Marking for worlds other than the main world.
DCHECK(ThreadState::Current()->GetIsolate());
for (DOMWrapperWorld* world : GetWorldMap().Values()) {
DOMDataStore& data_store = world->DomDataStore();
if (data_store.ContainsWrapper(script_wrappable))
data_store.Trace(script_wrappable, visitor);
}
}
DOMWrapperWorld::~DOMWrapperWorld() {
DCHECK(!IsMainWorld());
if (IsMainThread())
number_of_non_main_worlds_in_main_thread_--;
// WorkerWorld should be disposed of before the dtor.
if (!IsWorkerWorld())
Dispose();
DCHECK(!GetWorldMap().Contains(world_id_));
}
void DOMWrapperWorld::Dispose() {
dom_object_holders_.clear();
dom_data_store_.reset();
DCHECK(GetWorldMap().Contains(world_id_));
GetWorldMap().erase(world_id_);
}
scoped_refptr<DOMWrapperWorld> DOMWrapperWorld::EnsureIsolatedWorld(
v8::Isolate* isolate,
int world_id) {
#if DCHECK_IS_ON()
DCHECK(IsIsolatedWorldId(world_id));
#endif
WorldMap& map = GetWorldMap();
auto it = map.find(world_id);
if (it != map.end()) {
scoped_refptr<DOMWrapperWorld> world = it->value;
DCHECK(world->IsIsolatedWorld());
DCHECK_EQ(world_id, world->GetWorldId());
return world;
}
return base::AdoptRef(
new DOMWrapperWorld(isolate, WorldType::kIsolated, world_id));
}
typedef HashMap<int, scoped_refptr<SecurityOrigin>>
IsolatedWorldSecurityOriginMap;
static IsolatedWorldSecurityOriginMap& IsolatedWorldSecurityOrigins() {
DCHECK(IsMainThread());
DEFINE_STATIC_LOCAL(IsolatedWorldSecurityOriginMap, map, ());
return map;
}
SecurityOrigin* DOMWrapperWorld::IsolatedWorldSecurityOrigin() {
DCHECK(this->IsIsolatedWorld());
IsolatedWorldSecurityOriginMap& origins = IsolatedWorldSecurityOrigins();
IsolatedWorldSecurityOriginMap::iterator it = origins.find(GetWorldId());
return it == origins.end() ? nullptr : it->value.get();
}
void DOMWrapperWorld::SetIsolatedWorldSecurityOrigin(
int world_id,
scoped_refptr<SecurityOrigin> security_origin) {
#if DCHECK_IS_ON()
DCHECK(IsIsolatedWorldId(world_id));
#endif
if (security_origin)
IsolatedWorldSecurityOrigins().Set(world_id, std::move(security_origin));
else
IsolatedWorldSecurityOrigins().erase(world_id);
}
typedef HashMap<int, String> IsolatedWorldHumanReadableNameMap;
static IsolatedWorldHumanReadableNameMap& IsolatedWorldHumanReadableNames() {
DCHECK(IsMainThread());
DEFINE_STATIC_LOCAL(IsolatedWorldHumanReadableNameMap, map, ());
return map;
}
String DOMWrapperWorld::NonMainWorldHumanReadableName() {
DCHECK(!this->IsMainWorld());
return IsolatedWorldHumanReadableNames().at(GetWorldId());
}
void DOMWrapperWorld::SetNonMainWorldHumanReadableName(
int world_id,
const String& human_readable_name) {
#if DCHECK_IS_ON()
DCHECK(!IsMainWorldId(world_id));
#endif
IsolatedWorldHumanReadableNames().Set(world_id, human_readable_name);
}
void DOMWrapperWorld::RegisterDOMObjectHolderInternal(
std::unique_ptr<DOMObjectHolderBase> holder_base) {
DCHECK(!dom_object_holders_.Contains(holder_base.get()));
holder_base->SetWorld(this);
holder_base->SetWeak(&DOMWrapperWorld::WeakCallbackForDOMObjectHolder);
dom_object_holders_.insert(std::move(holder_base));
}
void DOMWrapperWorld::UnregisterDOMObjectHolder(
DOMObjectHolderBase* holder_base) {
DCHECK(dom_object_holders_.Contains(holder_base));
dom_object_holders_.erase(holder_base);
}
void DOMWrapperWorld::WeakCallbackForDOMObjectHolder(
const v8::WeakCallbackInfo<DOMObjectHolderBase>& data) {
DOMObjectHolderBase* holder_base = data.GetParameter();
holder_base->World()->UnregisterDOMObjectHolder(holder_base);
}
// static
int DOMWrapperWorld::GenerateWorldIdForType(WorldType world_type) {
DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<int>, next_world_id, ());
if (!next_world_id.IsSet())
*next_world_id = WorldId::kUnspecifiedWorldIdStart;
switch (world_type) {
case WorldType::kMain:
return kMainWorldId;
case WorldType::kIsolated:
// This function should not be called for IsolatedWorld because an
// identifier for the world is given from out of DOMWrapperWorld.
NOTREACHED();
return kInvalidWorldId;
case WorldType::kInspectorIsolated: {
DCHECK(IsMainThread());
static int next_devtools_isolated_world_id =
IsolatedWorldId::kDevToolsFirstIsolatedWorldId;
if (next_devtools_isolated_world_id >
IsolatedWorldId::kDevToolsLastIsolatedWorldId)
return WorldId::kInvalidWorldId;
return next_devtools_isolated_world_id++;
}
case WorldType::kRegExp:
case WorldType::kTesting:
case WorldType::kForV8ContextSnapshotNonMain:
case WorldType::kWorker:
int world_id = *next_world_id;
CHECK_GE(world_id, WorldId::kUnspecifiedWorldIdStart);
*next_world_id = world_id + 1;
return world_id;
}
NOTREACHED();
return kInvalidWorldId;
}
void DOMWrapperWorld::DissociateDOMWindowWrappersInAllWorlds(
ScriptWrappable* script_wrappable) {
DCHECK(script_wrappable);
DCHECK(IsMainThread());
script_wrappable->UnsetWrapperIfAny();
for (auto*& world : GetWorldMap().Values())
world->DomDataStore().UnsetWrapperIfAny(script_wrappable);
}
bool DOMWrapperWorld::HasWrapperInAnyWorldInMainThread(
ScriptWrappable* script_wrappable) {
DCHECK(IsMainThread());
Vector<scoped_refptr<DOMWrapperWorld>> worlds;
DOMWrapperWorld::AllWorldsInCurrentThread(worlds);
for (const auto& world : worlds) {
DOMDataStore& dom_data_store = world->DomDataStore();
if (dom_data_store.ContainsWrapper(script_wrappable))
return true;
}
return false;
}
} // namespace blink