1 // Copyright 2018 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 
5 #include "third_party/blink/renderer/core/fileapi/public_url_manager.h"
6 
7 #include "mojo/public/cpp/bindings/associated_receiver.h"
8 #include "mojo/public/cpp/bindings/pending_remote.h"
9 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 #include "third_party/blink/public/common/features.h"
12 #include "third_party/blink/renderer/core/fileapi/url_registry.h"
13 #include "third_party/blink/renderer/core/testing/null_execution_context.h"
14 #include "third_party/blink/renderer/platform/blob/testing/fake_blob.h"
15 #include "third_party/blink/renderer/platform/blob/testing/fake_blob_url_store.h"
16 #include "third_party/blink/renderer/platform/heap/persistent.h"
17 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
18 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
19 
20 namespace blink {
21 namespace {
22 
23 using mojom::blink::BlobURLStore;
24 
25 class TestURLRegistrable : public URLRegistrable {
26  public:
TestURLRegistrable(URLRegistry * registry,mojo::PendingRemote<mojom::blink::Blob> blob=mojo::NullRemote ())27   TestURLRegistrable(
28       URLRegistry* registry,
29       mojo::PendingRemote<mojom::blink::Blob> blob = mojo::NullRemote())
30       : registry_(registry), blob_(std::move(blob)) {}
31 
Registry() const32   URLRegistry& Registry() const override { return *registry_; }
33 
IsMojoBlob()34   bool IsMojoBlob() override { return bool{blob_}; }
35 
CloneMojoBlob(mojo::PendingReceiver<mojom::blink::Blob> receiver)36   void CloneMojoBlob(
37       mojo::PendingReceiver<mojom::blink::Blob> receiver) override {
38     if (!blob_)
39       return;
40     blob_->Clone(std::move(receiver));
41   }
42 
43  private:
44   URLRegistry* const registry_;
45   mojo::Remote<mojom::blink::Blob> blob_;
46 };
47 
48 class FakeURLRegistry : public URLRegistry {
49  public:
RegisterURL(SecurityOrigin * origin,const KURL & url,URLRegistrable * registrable)50   void RegisterURL(SecurityOrigin* origin,
51                    const KURL& url,
52                    URLRegistrable* registrable) override {
53     registrations.push_back(Registration{origin, url, registrable});
54   }
UnregisterURL(const KURL &)55   void UnregisterURL(const KURL&) override {}
56 
57   struct Registration {
58     SecurityOrigin* origin;
59     KURL url;
60     URLRegistrable* registrable;
61   };
62   Vector<Registration> registrations;
63 };
64 
65 }  // namespace
66 
67 class PublicURLManagerTest : public testing::Test {
68  public:
PublicURLManagerTest()69   PublicURLManagerTest() : url_store_receiver_(&url_store_) {}
70 
SetUp()71   void SetUp() override {
72     execution_context_ = MakeGarbageCollected<NullExecutionContext>();
73     // By default this creates a unique origin, which is exactly what this test
74     // wants.
75     execution_context_->SetUpSecurityContextForTesting();
76 
77     HeapMojoAssociatedRemote<BlobURLStore> url_store_remote(execution_context_);
78     url_store_receiver_.Bind(
79         url_store_remote.BindNewEndpointAndPassDedicatedReceiver());
80     url_manager().SetURLStoreForTesting(std::move(url_store_remote));
81   }
82 
url_manager()83   PublicURLManager& url_manager() {
84     return execution_context_->GetPublicURLManager();
85   }
86 
CreateMojoBlob(const String & uuid)87   mojo::PendingRemote<mojom::blink::Blob> CreateMojoBlob(const String& uuid) {
88     mojo::PendingRemote<mojom::blink::Blob> result;
89     mojo::MakeSelfOwnedReceiver(std::make_unique<FakeBlob>(uuid),
90                                 result.InitWithNewPipeAndPassReceiver());
91     return result;
92   }
93 
94  protected:
95   Persistent<NullExecutionContext> execution_context_;
96 
97   FakeBlobURLStore url_store_;
98   mojo::AssociatedReceiver<BlobURLStore> url_store_receiver_;
99 };
100 
TEST_F(PublicURLManagerTest,RegisterNonMojoBlob)101 TEST_F(PublicURLManagerTest, RegisterNonMojoBlob) {
102   FakeURLRegistry registry;
103   TestURLRegistrable registrable(&registry);
104   String url = url_manager().RegisterURL(&registrable);
105   ASSERT_EQ(1u, registry.registrations.size());
106   EXPECT_EQ(0u, url_store_.registrations.size());
107   EXPECT_EQ(execution_context_->GetSecurityOrigin(),
108             registry.registrations[0].origin);
109   EXPECT_EQ(url, registry.registrations[0].url);
110   EXPECT_EQ(&registrable, registry.registrations[0].registrable);
111 
112   EXPECT_TRUE(SecurityOrigin::CreateFromString(url)->IsSameOriginWith(
113       execution_context_->GetSecurityOrigin()));
114   EXPECT_EQ(execution_context_->GetSecurityOrigin(),
115             SecurityOrigin::CreateFromString(url));
116 
117   url_manager().Revoke(KURL(url));
118   EXPECT_FALSE(SecurityOrigin::CreateFromString(url)->IsSameOriginWith(
119       execution_context_->GetSecurityOrigin()));
120   url_store_receiver_.FlushForTesting();
121   // Even though this was not a mojo blob, the PublicURLManager might not know
122   // that, so still expect a revocation on the mojo interface.
123   ASSERT_EQ(1u, url_store_.revocations.size());
124   EXPECT_EQ(url, url_store_.revocations[0]);
125 }
126 
TEST_F(PublicURLManagerTest,RegisterMojoBlob)127 TEST_F(PublicURLManagerTest, RegisterMojoBlob) {
128   FakeURLRegistry registry;
129   TestURLRegistrable registrable(&registry, CreateMojoBlob("id"));
130   String url = url_manager().RegisterURL(&registrable);
131 
132   EXPECT_EQ(0u, registry.registrations.size());
133   ASSERT_EQ(1u, url_store_.registrations.size());
134   EXPECT_EQ(url, url_store_.registrations.begin()->key);
135 
136   EXPECT_TRUE(SecurityOrigin::CreateFromString(url)->IsSameOriginWith(
137       execution_context_->GetSecurityOrigin()));
138   EXPECT_EQ(execution_context_->GetSecurityOrigin(),
139             SecurityOrigin::CreateFromString(url));
140 
141   url_manager().Revoke(KURL(url));
142   EXPECT_FALSE(SecurityOrigin::CreateFromString(url)->IsSameOriginWith(
143       execution_context_->GetSecurityOrigin()));
144   url_store_receiver_.FlushForTesting();
145   ASSERT_EQ(1u, url_store_.revocations.size());
146   EXPECT_EQ(url, url_store_.revocations[0]);
147 }
148 
TEST_F(PublicURLManagerTest,RevokeValidNonRegisteredURL)149 TEST_F(PublicURLManagerTest, RevokeValidNonRegisteredURL) {
150   execution_context_->SetURL(KURL("http://example.com/foo/bar"));
151   execution_context_->SetUpSecurityContextForTesting();
152 
153   KURL url = KURL("blob:http://example.com/id");
154   url_manager().Revoke(url);
155   url_store_receiver_.FlushForTesting();
156   ASSERT_EQ(1u, url_store_.revocations.size());
157   EXPECT_EQ(url, url_store_.revocations[0]);
158 }
159 
TEST_F(PublicURLManagerTest,RevokeInvalidURL)160 TEST_F(PublicURLManagerTest, RevokeInvalidURL) {
161   execution_context_->SetURL(KURL("http://example.com/foo/bar"));
162   execution_context_->SetUpSecurityContextForTesting();
163 
164   KURL invalid_scheme_url = KURL("blb:http://example.com/id");
165   KURL fragment_url = KURL("blob:http://example.com/id#fragment");
166   KURL invalid_origin_url = KURL("blob:http://foobar.com/id");
167   url_manager().Revoke(invalid_scheme_url);
168   url_manager().Revoke(fragment_url);
169   url_manager().Revoke(invalid_origin_url);
170   url_store_receiver_.FlushForTesting();
171   // Both should have been silently ignored.
172   EXPECT_TRUE(url_store_.revocations.IsEmpty());
173 }
174 
175 }  // namespace blink
176