api_binding_util.cc 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. // Copyright 2017 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "extensions/renderer/bindings/api_binding_util.h"
  5. #include "base/check_op.h"
  6. #include "base/notreached.h"
  7. #include "base/observer_list.h"
  8. #include "base/supports_user_data.h"
  9. #include "build/build_config.h"
  10. #include "build/chromeos_buildflags.h"
  11. #include "extensions/renderer/bindings/get_per_context_data.h"
  12. #include "extensions/renderer/bindings/js_runner.h"
  13. #include "gin/converter.h"
  14. #include "gin/per_context_data.h"
  15. namespace extensions {
  16. namespace binding {
  17. namespace {
  18. bool g_response_validation_enabled =
  19. #if DCHECK_IS_ON()
  20. true;
  21. #else
  22. false;
  23. #endif
  24. } // namespace
  25. class ContextInvalidationData : public base::SupportsUserData::Data {
  26. public:
  27. ContextInvalidationData();
  28. ContextInvalidationData(const ContextInvalidationData&) = delete;
  29. ContextInvalidationData& operator=(const ContextInvalidationData&) = delete;
  30. ~ContextInvalidationData() override;
  31. static constexpr char kPerContextDataKey[] = "extension_context_invalidation";
  32. void Invalidate();
  33. void AddListener(ContextInvalidationListener* listener);
  34. void RemoveListener(ContextInvalidationListener* listener);
  35. bool is_context_valid() const { return is_context_valid_; }
  36. private:
  37. bool is_context_valid_ = true;
  38. base::ObserverList<ContextInvalidationListener>::Unchecked
  39. invalidation_listeners_;
  40. };
  41. constexpr char ContextInvalidationData::kPerContextDataKey[];
  42. ContextInvalidationData::ContextInvalidationData() = default;
  43. ContextInvalidationData::~ContextInvalidationData() {
  44. if (is_context_valid_)
  45. Invalidate();
  46. }
  47. void ContextInvalidationData::AddListener(
  48. ContextInvalidationListener* listener) {
  49. DCHECK(is_context_valid_);
  50. invalidation_listeners_.AddObserver(listener);
  51. }
  52. void ContextInvalidationData::RemoveListener(
  53. ContextInvalidationListener* listener) {
  54. DCHECK(is_context_valid_);
  55. DCHECK(invalidation_listeners_.HasObserver(listener));
  56. invalidation_listeners_.RemoveObserver(listener);
  57. }
  58. void ContextInvalidationData::Invalidate() {
  59. DCHECK(is_context_valid_);
  60. is_context_valid_ = false;
  61. for (ContextInvalidationListener& listener : invalidation_listeners_)
  62. listener.OnInvalidated();
  63. }
  64. bool IsContextValid(v8::Local<v8::Context> context) {
  65. gin::PerContextData* per_context_data = gin::PerContextData::From(context);
  66. if (!per_context_data)
  67. return false;
  68. auto* invalidation_data =
  69. static_cast<ContextInvalidationData*>(per_context_data->GetUserData(
  70. ContextInvalidationData::kPerContextDataKey));
  71. // The context is valid if we've never created invalidation data for it, or if
  72. // we have and it hasn't been marked as invalid.
  73. bool is_context_valid =
  74. !invalidation_data || invalidation_data->is_context_valid();
  75. if (is_context_valid) {
  76. // As long as the context is valid, there should be an associated
  77. // JSRunner.
  78. // TODO(devlin): (Likely) Remove this once https://crbug.com/819968, since
  79. // this shouldn't necessarily be a hard dependency. At least downgrade it
  80. // to a DCHECK.
  81. CHECK(JSRunner::Get(context));
  82. }
  83. return is_context_valid;
  84. }
  85. bool IsContextValidOrThrowError(v8::Local<v8::Context> context) {
  86. if (IsContextValid(context))
  87. return true;
  88. v8::Isolate* isolate = context->GetIsolate();
  89. isolate->ThrowException(v8::Exception::Error(
  90. gin::StringToV8(isolate, "Extension context invalidated.")));
  91. return false;
  92. }
  93. void InvalidateContext(v8::Local<v8::Context> context) {
  94. ContextInvalidationData* data =
  95. GetPerContextData<ContextInvalidationData>(context, kCreateIfMissing);
  96. if (!data)
  97. return;
  98. data->Invalidate();
  99. }
  100. std::string GetPlatformString() {
  101. // TODO(https://crbug.com/1052397): For readability, this should become
  102. // BUILDFLAG(IS_CHROMEOS) && BUILDFLAG(IS_CHROMEOS_LACROS). The second
  103. // conditional should be BUILDFLAG(IS_CHROMEOS) && BUILDFLAG(IS_CHROMEOS_ASH).
  104. #if BUILDFLAG(IS_CHROMEOS_LACROS)
  105. return "lacros";
  106. #elif BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)
  107. return "chromeos";
  108. #elif BUILDFLAG(IS_LINUX)
  109. return "linux";
  110. #elif BUILDFLAG(IS_MAC)
  111. return "mac";
  112. #elif BUILDFLAG(IS_WIN)
  113. return "win";
  114. #elif BUILDFLAG(IS_FUCHSIA)
  115. return "fuchsia";
  116. #else
  117. NOTREACHED();
  118. return std::string();
  119. #endif
  120. }
  121. ContextInvalidationListener::ContextInvalidationListener(
  122. v8::Local<v8::Context> context,
  123. base::OnceClosure on_invalidated)
  124. : on_invalidated_(std::move(on_invalidated)),
  125. context_invalidation_data_(
  126. GetPerContextData<ContextInvalidationData>(context,
  127. kCreateIfMissing)) {
  128. // We should never add an invalidation observer to an invalid context.
  129. DCHECK(context_invalidation_data_);
  130. DCHECK(context_invalidation_data_->is_context_valid());
  131. context_invalidation_data_->AddListener(this);
  132. }
  133. ContextInvalidationListener::~ContextInvalidationListener() {
  134. if (!on_invalidated_)
  135. return; // Context was invalidated.
  136. DCHECK(context_invalidation_data_);
  137. context_invalidation_data_->RemoveListener(this);
  138. }
  139. void ContextInvalidationListener::OnInvalidated() {
  140. DCHECK(on_invalidated_);
  141. context_invalidation_data_ = nullptr;
  142. std::move(on_invalidated_).Run();
  143. }
  144. bool IsResponseValidationEnabled() {
  145. return g_response_validation_enabled;
  146. }
  147. std::unique_ptr<base::AutoReset<bool>> SetResponseValidationEnabledForTesting(
  148. bool is_enabled) {
  149. return std::make_unique<base::AutoReset<bool>>(&g_response_validation_enabled,
  150. is_enabled);
  151. }
  152. } // namespace binding
  153. } // namespace extensions