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
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189

content / browser / service_worker / service_worker_update_checker.h [blame]

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_UPDATE_CHECKER_H_
#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_UPDATE_CHECKER_H_

#include <map>
#include <memory>
#include <vector>

#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "content/browser/renderer_host/policy_container_host.h"
#include "content/browser/service_worker/service_worker_single_script_update_checker.h"
#include "content/browser/service_worker/service_worker_updated_script_loader.h"
#include "content/common/content_export.h"

namespace network {
class SharedURLLoaderFactory;
}

namespace content {

class ServiceWorkerContextCore;
class ServiceWorkerVersion;

// This is responsible for byte-for-byte update checking. Mostly corresponding
// to step 1-9 in [[Update]] in the spec, but this stops to fetch scripts after
// any changes found.
// https://w3c.github.io/ServiceWorker/#update-algorithm
//
// This is owned and used by ServiceWorkerRegisterJob as a part of the update
// logic.
class CONTENT_EXPORT ServiceWorkerUpdateChecker {
 public:
  // Data of each compared script needed in remaining update process
  struct CONTENT_EXPORT ComparedScriptInfo {
    ComparedScriptInfo();
    ComparedScriptInfo(
        int64_t old_resource_id,
        ServiceWorkerSingleScriptUpdateChecker::Result result,
        std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker::PausedState>
            paused_state,
        std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker::FailureInfo>
            failure_info);
    ComparedScriptInfo(ComparedScriptInfo&& other);
    ComparedScriptInfo& operator=(ComparedScriptInfo&& other);
    ~ComparedScriptInfo();

    // Resource id of the compared script already in storage
    int64_t old_resource_id;

    // Compare result of a single script
    ServiceWorkerSingleScriptUpdateChecker::Result result;

    // This is only valid for script compared to be different. It consists of
    // some state variables to continue the update process, including
    // ServiceWorkerCacheWriter, and Mojo endpoints for downloading.
    std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker::PausedState>
        paused_state;

    // This is set when |result| is kFailed. This is used to keep the error code
    // which the update checker saw to the renderer when installing the worker.
    std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker::FailureInfo>
        failure_info;
  };

  // This is to notify the update check result. Value of |result| can be:
  // 1. ServiceWorkerSingleScriptUpdateChecker::Result::kIdentical
  //    All the scripts are identical with existing version, no need to update.
  //    |failure_info| is nullptr.
  // 2. ServiceWorkerSingleScriptUpdateChecker::Result::kDifferent
  //    Some script changed, update is needed. |failure_info| is nullptr.
  // 3. ServiceWorkerSingleScriptUpdateChecker::Result::kFailed
  //    The update check failed, detailed error info is in |failure_info|.
  //
  // |updated_sha256_script_checksums| contains the pair of updated hash string
  // and its script url. It's normally empty, except for the case when
  // |main_script_sha256_checksum_| has a value, and the update check finished
  // with kIdentical.
  using UpdateStatusCallback = base::OnceCallback<void(
      ServiceWorkerSingleScriptUpdateChecker::Result result,
      std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker::FailureInfo>
          failure_info,
      const std::map<GURL, std::string>& updated_sha256_script_checksums)>;

  ServiceWorkerUpdateChecker() = delete;

  ServiceWorkerUpdateChecker(
      std::vector<storage::mojom::ServiceWorkerResourceRecordPtr>
          scripts_to_compare,
      const GURL& main_script_url,
      int64_t main_script_resource_id,
      const std::optional<std::string>& main_script_sha256_checksum,
      scoped_refptr<ServiceWorkerVersion> version_to_update,
      scoped_refptr<network::SharedURLLoaderFactory> loader_factory,
      bool force_bypass_cache,
      blink::mojom::ScriptType worker_script_type,
      blink::mojom::ServiceWorkerUpdateViaCache update_via_cache,
      base::TimeDelta time_since_last_check,
      ServiceWorkerContextCore* context,
      blink::mojom::FetchClientSettingsObjectPtr fetch_client_settings_object);

  ServiceWorkerUpdateChecker(const ServiceWorkerUpdateChecker&) = delete;
  ServiceWorkerUpdateChecker& operator=(const ServiceWorkerUpdateChecker&) =
      delete;

  ~ServiceWorkerUpdateChecker();

  // |callback| is always triggered when the update check finishes.
  void Start(UpdateStatusCallback callback);

  // This transfers the ownership of the check result to the caller. It only
  // contains the information about scripts which have already been compared.
  std::map<GURL, ComparedScriptInfo> TakeComparedResults();

  void OnOneUpdateCheckFinished(
      int64_t old_resource_id,
      const GURL& script_url,
      ServiceWorkerSingleScriptUpdateChecker::Result result,
      std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker::FailureInfo>
          failure_info,
      std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker::PausedState>
          paused_state,
      const std::optional<std::string>& sha256_checksum);

  const GURL& updated_script_url() const { return updated_script_url_; }
  bool network_accessed() const { return network_accessed_; }
  const scoped_refptr<PolicyContainerHost> policy_container_host() const {
    return policy_container_host_;
  }

 private:
  void CheckOneScript(const GURL& url, const int64_t resource_id);
  void OnResourceIdAssignedForOneScriptCheck(const GURL& url,
                                             const int64_t resource_id,
                                             const int64_t new_resource_id);

  const GURL main_script_url_;
  const int64_t main_script_resource_id_;
  const std::optional<std::string> main_script_sha256_checksum_;

  std::vector<storage::mojom::ServiceWorkerResourceRecordPtr>
      scripts_to_compare_;
  size_t next_script_index_to_compare_ = 0;
  std::map<GURL, ComparedScriptInfo> script_check_results_;

  // The URL of the script for which a byte-for-byte change was found, or else
  // the empty GURL if there is no such script.
  GURL updated_script_url_;

  // The version which triggered this update.
  scoped_refptr<ServiceWorkerVersion> version_to_update_;

  std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker> running_checker_;

  UpdateStatusCallback callback_;

  scoped_refptr<network::SharedURLLoaderFactory> loader_factory_;

  const bool force_bypass_cache_;
  const blink::mojom::ScriptType worker_script_type_;
  const blink::mojom::ServiceWorkerUpdateViaCache update_via_cache_;
  const base::TimeDelta time_since_last_check_;

  // True if any at least one of the scripts is fetched by network.
  bool network_accessed_ = false;

  // Contains the pair of updated hash string and its script url.
  // This manages newly calculated checksum strings so that we store them on
  // memory and database.
  std::map<GURL, std::string> updated_sha256_script_checksums_;

  scoped_refptr<PolicyContainerHost> policy_container_host_;

  // |context_| outlives |this| because it owns |this| through
  // ServiceWorkerJobCoordinator and ServiceWorkerRegisterJob.
  const raw_ptr<ServiceWorkerContextCore> context_;

  blink::mojom::FetchClientSettingsObjectPtr fetch_client_settings_object_;

  base::WeakPtrFactory<ServiceWorkerUpdateChecker> weak_factory_{this};
};

}  // namespace content

#endif  // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_UPDATE_CHECKER_H_