From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Adam Rice <ricea@chromium.org>
Date: Sat, 6 Nov 2021 18:52:26 +0000
Subject: Return undefined from UnderlyingSinkBase "type" getter

The "type" attribute of an object passed to the WritableStream
constructor is supposed to return undefined. Add a getter to
UnderlyingSinkBase to ensure it always does.

Add tests to verify that "type" is not inherited from
Object.prototype.type.

Move some methods out-of-line into a new underlying_sink_base.cc file.

Make WritableStreamDefaultController::From() more robust.

BUG=1262791

(cherry picked from commit 26564c88bc9e034cc512afd857cf303193647b9a)

Change-Id: I97f43233eef0e473fb1a22a3ea8afafe92e16266
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3252171
Reviewed-by: Yutaka Hirano <yhirano@chromium.org>
Commit-Queue: Adam Rice <ricea@chromium.org>
Cr-Original-Commit-Position: refs/heads/main@{#936834}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3266824
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Cr-Commit-Position: refs/branch-heads/4664@{#806}
Cr-Branched-From: 24dc4ee75e01a29d390d43c9c264372a169273a7-refs/heads/main@{#929512}

diff --git a/third_party/blink/renderer/core/streams/build.gni b/third_party/blink/renderer/core/streams/build.gni
index 57a106353bd6720bc7da88f96dce66e4332aea2a..744d47f1a1e9352cc4df56ce89635b3d6f8b5425 100644
--- a/third_party/blink/renderer/core/streams/build.gni
+++ b/third_party/blink/renderer/core/streams/build.gni
@@ -43,6 +43,7 @@ blink_core_sources_streams = [
   "transform_stream_default_controller.cc",
   "transform_stream_default_controller.h",
   "transform_stream_transformer.h",
+  "underlying_sink_base.cc",
   "underlying_sink_base.h",
   "underlying_source_base.cc",
   "underlying_source_base.h",
diff --git a/third_party/blink/renderer/core/streams/underlying_sink_base.cc b/third_party/blink/renderer/core/streams/underlying_sink_base.cc
new file mode 100644
index 0000000000000000000000000000000000000000..a10d4f1865fc4e11c90f463b64ff666cce0fba69
--- /dev/null
+++ b/third_party/blink/renderer/core/streams/underlying_sink_base.cc
@@ -0,0 +1,29 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/streams/underlying_sink_base.h"
+
+#include "third_party/blink/renderer/core/streams/writable_stream_default_controller.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+ScriptPromise UnderlyingSinkBase::start(ScriptState* script_state,
+                                        ScriptValue controller,
+                                        ExceptionState& exception_state) {
+  controller_ = WritableStreamDefaultController::From(script_state, controller);
+  return start(script_state, controller_, exception_state);
+}
+
+ScriptValue UnderlyingSinkBase::type(ScriptState* script_state) const {
+  auto* isolate = script_state->GetIsolate();
+  return ScriptValue(isolate, v8::Undefined(isolate));
+}
+
+void UnderlyingSinkBase::Trace(Visitor* visitor) const {
+  visitor->Trace(controller_);
+  ScriptWrappable::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/streams/underlying_sink_base.h b/third_party/blink/renderer/core/streams/underlying_sink_base.h
index 3b07d87f064a26e01c4ee827d78fca005049c90a..07ba729fc3a78cf2102e2d64e527048af8be9c71 100644
--- a/third_party/blink/renderer/core/streams/underlying_sink_base.h
+++ b/third_party/blink/renderer/core/streams/underlying_sink_base.h
@@ -6,16 +6,20 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_STREAMS_UNDERLYING_SINK_BASE_H_
 
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
 #include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/streams/writable_stream_default_controller.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/heap/visitor.h"
 
+// Various files depend on us exporting this header.
+// TODO(ricea): Clean up the dependencies and remove this include.
+#include "third_party/blink/renderer/core/streams/writable_stream_default_controller.h"
+
 namespace blink {
 
 class ExceptionState;
-class ScriptValue;
 class ScriptState;
+class WritableStreamDefaultController;
 
 class CORE_EXPORT UnderlyingSinkBase : public ScriptWrappable {
   DEFINE_WRAPPERTYPEINFO();
@@ -38,12 +42,8 @@ class CORE_EXPORT UnderlyingSinkBase : public ScriptWrappable {
                               ScriptValue reason,
                               ExceptionState&) = 0;
 
-  ScriptPromise start(ScriptState* script_state,
-                      ScriptValue controller,
-                      ExceptionState& exception_state) {
-    controller_ = WritableStreamDefaultController::From(controller);
-    return start(script_state, controller_, exception_state);
-  }
+  ScriptPromise start(ScriptState*, ScriptValue controller, ExceptionState&);
+
   ScriptPromise write(ScriptState* script_state,
                       ScriptValue chunk,
                       ScriptValue controller,
@@ -52,10 +52,11 @@ class CORE_EXPORT UnderlyingSinkBase : public ScriptWrappable {
     return write(script_state, chunk, controller_, exception_state);
   }
 
-  void Trace(Visitor* visitor) const override {
-    visitor->Trace(controller_);
-    ScriptWrappable::Trace(visitor);
-  }
+  // Returns a JavaScript "undefined" value. This is required by the
+  // WritableStream Create() method.
+  ScriptValue type(ScriptState*) const;
+
+  void Trace(Visitor*) const override;
 
  protected:
   WritableStreamDefaultController* Controller() const { return controller_; }
diff --git a/third_party/blink/renderer/core/streams/underlying_sink_base.idl b/third_party/blink/renderer/core/streams/underlying_sink_base.idl
index 8351141cbc07ed32566a0006f998aab61e8fd6b5..470eb527b11e3dce355d724eaa63ec981b7788e4 100644
--- a/third_party/blink/renderer/core/streams/underlying_sink_base.idl
+++ b/third_party/blink/renderer/core/streams/underlying_sink_base.idl
@@ -14,4 +14,7 @@ interface UnderlyingSinkBase {
     [CallWith=ScriptState, RaisesException] Promise<void> write(any chunk, any controller);
     [CallWith=ScriptState, RaisesException] Promise<void> close();
     [CallWith=ScriptState, RaisesException] Promise<void> abort(any reason);
+
+    // This only exists to prevent Object.prototype.type being accessed.
+    [CallWith=ScriptState] readonly attribute any type;
 };
diff --git a/third_party/blink/renderer/core/streams/writable_stream_default_controller.cc b/third_party/blink/renderer/core/streams/writable_stream_default_controller.cc
index 0b22eedcddc87b0ac695389a76ed5e353dd5f92c..bc959b6c33dbba6a367b4d247d56b67698299635 100644
--- a/third_party/blink/renderer/core/streams/writable_stream_default_controller.cc
+++ b/third_party/blink/renderer/core/streams/writable_stream_default_controller.cc
@@ -20,10 +20,14 @@
 namespace blink {
 
 WritableStreamDefaultController* WritableStreamDefaultController::From(
+    ScriptState* script_state,
     ScriptValue controller) {
-  DCHECK(controller.IsObject());
-  return V8WritableStreamDefaultController::ToImpl(
-      controller.V8Value().As<v8::Object>());
+  CHECK(controller.IsObject());
+  auto* controller_impl =
+      V8WritableStreamDefaultController::ToImplWithTypeCheck(
+          script_state->GetIsolate(), controller.V8Value().As<v8::Object>());
+  CHECK(controller_impl);
+  return controller_impl;
 }
 
 // Only used internally. Not reachable from JavaScript.
diff --git a/third_party/blink/renderer/core/streams/writable_stream_default_controller.h b/third_party/blink/renderer/core/streams/writable_stream_default_controller.h
index 3351dcd6941360fc9fecaa8c03080da813a5b6a2..8be676421763caa911b8d003c869856675e28713 100644
--- a/third_party/blink/renderer/core/streams/writable_stream_default_controller.h
+++ b/third_party/blink/renderer/core/streams/writable_stream_default_controller.h
@@ -27,7 +27,7 @@ class CORE_EXPORT WritableStreamDefaultController final
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static WritableStreamDefaultController* From(ScriptValue);
+  static WritableStreamDefaultController* From(ScriptState*, ScriptValue);
 
   // The JavaScript-exposed constructor throws automatically as no constructor
   // is specified in the IDL. This constructor is used internally during
diff --git a/third_party/blink/web_tests/http/tests/streams/chromium/underlying-sink-base-type-getter.html b/third_party/blink/web_tests/http/tests/streams/chromium/underlying-sink-base-type-getter.html
new file mode 100644
index 0000000000000000000000000000000000000000..fbbc26b08ea4e310ed99b7a02e1a7952e8f60921
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/streams/chromium/underlying-sink-base-type-getter.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+'use strict';
+
+test(t => {
+  Object.defineProperty(Object.prototype, 'type',
+                        {
+                          configurable: true,
+                          get() {
+                            throw Error();
+                          }
+                        });
+  t.add_cleanup(() => {
+    delete Object.prototype.type;
+  });
+  const generator = new MediaStreamTrackGenerator('video');
+  // The WritableStream is created lazily, so access it to trigger creation.
+  generator.writable.getWriter();
+}, 'a throwing getter on Object.prototype.type should not interfere with ' +
+     'native writable stream creation');
+
+test(t => {
+  Object.defineProperty(Object.prototype, 'type',
+                        {
+                          configurable: true,
+                          get() {
+                            this.start(0x414141);
+                          }
+                        });
+  t.add_cleanup(() => {
+    delete Object.prototype.type;
+  });
+  const generator = new MediaStreamTrackGenerator('video');
+  generator.writable.getWriter();
+}, 'a getter that calls start() with a number on Object.prototype.type ' +
+     'should not interfere with native writable stream creation');
+
+test(t => {
+  Object.defineProperty(Object.prototype, 'type',
+                        {
+                          configurable: true,
+                          get() {
+                            this.start({});
+                          }
+                        });
+  t.add_cleanup(() => {
+    delete Object.prototype.type;
+  });
+  const generator = new MediaStreamTrackGenerator('video');
+  generator.writable.getWriter();
+}, 'a getter that calls start() with an object on Object.prototype.type ' +
+     'should not interfere with native writable stream creation');
+
+</script>
