summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/core/workers/worklet_module_responses_map.cc
blob: 940a822a3f3bae6609606a729323921314fa0a1a (plain)
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
// Copyright 2017 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/workers/worklet_module_responses_map.h"

#include "base/optional.h"
#include "third_party/blink/renderer/platform/cross_thread_functional.h"
#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"

namespace blink {

namespace {

bool IsValidURL(const KURL& url) {
  return !url.IsEmpty() && url.IsValid();
}

}  // namespace

void WorkletModuleResponsesMap::Entry::AddClient(
    ModuleScriptFetcher::Client* client,
    scoped_refptr<base::SingleThreadTaskRunner> client_task_runner) {
  // Clients can be added only while a module script is being fetched.
  DCHECK_EQ(state_, State::kFetching);
  clients_.insert(client, client_task_runner);
}

// Implementation of the second half of the custom fetch defined in the
// "fetch a worklet script" algorithm:
// https://drafts.css-houdini.org/worklets/#fetch-a-worklet-script
void WorkletModuleResponsesMap::Entry::SetParams(
    const base::Optional<ModuleScriptCreationParams>& params) {
  DCHECK_EQ(state_, State::kFetching);

  if (params) {
    state_ = State::kFetched;

    // Step 7: "Let response be the result of fetch when it asynchronously
    // completes."
    // Step 8: "Set the value of the entry in cache whose key is url to
    // response, and asynchronously complete this algorithm with response."
    params_.emplace(params->IsolatedCopy());
    DCHECK(params_->IsSafeToSendToAnotherThread());
    for (auto& it : clients_) {
      PostCrossThreadTask(
          *it.value, FROM_HERE,
          CrossThreadBind(&ModuleScriptFetcher::Client::OnFetched, it.key,
                          *params));
    }
  } else {
    state_ = State::kFailed;
    // TODO(nhiroki): Add |error_messages| to the context's message storage.
    for (auto& it : clients_) {
      PostCrossThreadTask(
          *it.value, FROM_HERE,
          CrossThreadBind(&ModuleScriptFetcher::Client::OnFailed, it.key));
    }
  }

  clients_.clear();
}

// Implementation of the first half of the custom fetch defined in the
// "fetch a worklet script" algorithm:
// https://drafts.css-houdini.org/worklets/#fetch-a-worklet-script
//
// "To perform the fetch given request, perform the following steps:"
// Step 1: "Let cache be the moduleResponsesMap."
// Step 2: "Let url be request's url."
bool WorkletModuleResponsesMap::GetEntry(
    const KURL& url,
    ModuleScriptFetcher::Client* client,
    scoped_refptr<base::SingleThreadTaskRunner> client_task_runner) {
  MutexLocker lock(mutex_);
  if (!is_available_ || !IsValidURL(url)) {
    client_task_runner->PostTask(
        FROM_HERE, WTF::Bind(&ModuleScriptFetcher::Client::OnFailed,
                             WrapPersistent(client)));
    return true;
  }

  auto it = entries_.find(url);
  if (it != entries_.end()) {
    Entry* entry = it->value.get();
    switch (entry->GetState()) {
      case Entry::State::kFetching:
        // Step 3: "If cache contains an entry with key url whose value is
        // "fetching", wait until that entry's value changes, then proceed to
        // the next step."
        entry->AddClient(client, client_task_runner);
        return true;
      case Entry::State::kFetched:
        // Step 4: "If cache contains an entry with key url, asynchronously
        // complete this algorithm with that entry's value, and abort these
        // steps."
        client_task_runner->PostTask(
            FROM_HERE, WTF::Bind(&ModuleScriptFetcher::Client::OnFetched,
                                 WrapPersistent(client), entry->GetParams()));
        return true;
      case Entry::State::kFailed:
        // Module fetching failed before. Abort following steps.
        client_task_runner->PostTask(
            FROM_HERE, WTF::Bind(&ModuleScriptFetcher::Client::OnFailed,
                                 WrapPersistent(client)));
        return true;
    }
    NOTREACHED();
  }

  // Step 5: "Create an entry in cache with key url and value "fetching"."
  std::unique_ptr<Entry> entry = std::make_unique<Entry>();
  entry->AddClient(client, client_task_runner);
  entries_.insert(url.Copy(), std::move(entry));

  // Step 6: "Fetch request."
  // Running the callback with an empty params will make the fetcher to fallback
  // to regular module loading and Write() will be called once the fetch is
  // complete.
  return false;
}

void WorkletModuleResponsesMap::SetEntryParams(
    const KURL& url,
    const base::Optional<ModuleScriptCreationParams>& params) {
  MutexLocker lock(mutex_);
  if (!is_available_)
    return;

  DCHECK(entries_.Contains(url));
  Entry* entry = entries_.find(url)->value.get();
  entry->SetParams(params);
}

void WorkletModuleResponsesMap::Dispose() {
  DCHECK(IsMainThread());
  MutexLocker lock(mutex_);
  is_available_ = false;
  for (auto& it : entries_) {
    switch (it.value->GetState()) {
      case Entry::State::kFetching:
        it.value->SetParams(base::nullopt);
        break;
      case Entry::State::kFetched:
      case Entry::State::kFailed:
        break;
    }
  }
  entries_.clear();
}

}  // namespace blink