File size: 4,918 Bytes
23443f5
39eaa9c
23443f5
5379ab8
 
39eaa9c
 
 
 
 
 
 
23443f5
 
5379ab8
 
39eaa9c
 
5379ab8
 
 
 
 
 
 
 
 
 
 
 
39eaa9c
5379ab8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39eaa9c
5379ab8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39eaa9c
5379ab8
 
 
 
 
 
 
 
 
39eaa9c
 
5379ab8
 
 
 
 
 
 
 
 
39eaa9c
5379ab8
 
 
 
 
39eaa9c
5379ab8
 
 
 
 
 
 
 
39eaa9c
5379ab8
39eaa9c
5379ab8
 
 
 
 
 
 
 
 
 
39eaa9c
 
5379ab8
 
 
39eaa9c
5379ab8
39eaa9c
5379ab8
39eaa9c
23443f5
39eaa9c
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
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8" />
  <title>1ファイル完結型 Service Worker via iframe+Blob</title>
  <style>
    body { font-family: sans-serif; padding: 1em; }
    #log { margin-top: 1em; font-size: 14px; }
    .success { color: green; margin-bottom: 1em; }
    .error { color: red; margin-bottom: 1em; }
    pre { background: #f9f9f9; padding: 0.5em; border: 1px solid #ccc; }
  </style>
</head>
<body>
  <h1>1ファイルで完結する Service Worker (iframe + Blob)</h1>
  <div id="log">Service Worker の登録を試みています…</div>

  <script>
    // iframe内のHTMLを文字列として用意(Service Workerスクリプト含む)
    const iframeHTML = `
      <!DOCTYPE html>
      <html>
      <head><title>SW iframe</title></head>
      <body>
        <script type="text/javascript">
          // Service Worker スクリプトを文字列で用意
          const swCode = \`
            self.addEventListener('fetch', event => {
              const url = event.request.url;
              const method = event.request.method;

              event.respondWith(
                fetch(event.request)
                  .then(response => {
                    const clone = response.clone();
                    clone.text().then(body => {
                      self.clients.matchAll().then(clients => {
                        clients.forEach(client => {
                          client.postMessage({
                            type: 'fetch-log',
                            url,
                            method,
                            status: response.status,
                            body: body.slice(0, 200)
                          });
                        });
                      });
                    });
                    return response;
                  })
                  .catch(error => {
                    self.clients.matchAll().then(clients => {
                      clients.forEach(client => {
                        client.postMessage({
                          type: 'fetch-error',
                          url,
                          method,
                          error: error.toString()
                        });
                      });
                    });
                    throw error;
                  })
              );
            });
          \`;

          // Blob URLとして作成
          const blob = new Blob([swCode], { type: 'application/javascript' });
          const swBlobUrl = URL.createObjectURL(blob);

          // Service Worker 登録
          if ('serviceWorker' in navigator) {
            navigator.serviceWorker.register(swBlobUrl)
              .then(() => {
                parent.postMessage({ type: 'sw-registered' }, '*');
              })
              .catch(err => {
                parent.postMessage({ type: 'sw-error', error: err.toString() }, '*');
              });

            // Service Worker からのメッセージを受け取り親に中継
            navigator.serviceWorker.addEventListener('message', event => {
              parent.postMessage({ type: 'sw-message', data: event.data }, '*');
            });
          }
        <\/script>
      </body>
      </html>
    `;

    // Blob URLとして iframe のページを作る
    const iframeBlob = new Blob([iframeHTML], { type: 'text/html' });
    const iframeUrl = URL.createObjectURL(iframeBlob);

    // iframe要素作成
    const iframe = document.createElement('iframe');
    iframe.src = iframeUrl;
    iframe.style.display = 'none'; // 表示不要
    document.body.appendChild(iframe);

    // メッセージ受け取り処理
    const logDiv = document.getElementById('log');
    window.addEventListener('message', event => {
      const data = event.data;
      if (!data || !data.type) return;

      if (data.type === 'sw-registered') {
        logDiv.textContent = '✅ Service Worker 登録完了 (iframe 内)';
      }
      else if (data.type === 'sw-error') {
        logDiv.textContent = '❌ Service Worker 登録失敗: ' + data.error;
      }
      else if (data.type === 'sw-message') {
        const msg = data.data;
        const div = document.createElement('div');
        div.className = msg.type === 'fetch-log' ? 'success' : 'error';

        if (msg.type === 'fetch-log') {
          div.innerHTML = \`
            ✅ [\${msg.method}] \${msg.url} — \${msg.status}<br>
            <pre>\${msg.body}</pre>
          \`;
        } else if (msg.type === 'fetch-error') {
          div.innerHTML = \`
            ❌ [\${msg.method}] \${msg.url}<br>
            エラー: \${msg.error}
          \`;
        }

        logDiv.appendChild(div);
      }
    });

    // 動作確認用fetchを親ページから実行
    fetch('https://jsonplaceholder.typicode.com/posts/1');
    fetch('https://httpstat.us/404');
  </script>
</body>
</html>