【Chrome拡張】URLデコードで丸見えだ👀✨

# ➊ はじめに
会社で**Teams**から**Sharepoint**の資料を参照する際、ブラウザに表示されるURI（Uniform Resource Identifier）が、「`https://example.com/?text=%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF/file.txt`」などとエンコードされていて、ファイルへのパス確認が難しいため、Chrome拡張のお勉強も兼ねて、複数バイトのURIをデコードする**Chrome拡張**を作ってみたくなりました。

![sample](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405376/c2fd8f78-2f88-34d2-555c-410d839b7e61.png)

:::note warn
ちなみに普通にブラウジングすると、ブラウザ上部のURI表示場所にはデコードされたURIが表示されますので、ここではイメージとしてUTF-16でエンコードされたURIを使っています。
:::

**➡これを使えば、エンコードされたURIからでもパスが丸見えだぜ！👀✨**

# ➋ URIエンコードについて
URIの標準であるRFC 3986によれば、**URI**（Uniform Resource Identifier）内の非ASCII文字や特殊文字はパーセントエンコーディングによってエンコードする必要があります。このエンコーディングにおいて、RFC 3986では文字の符号化にはUTF-8が標準とされています。つまり、非ASCII文字や特殊文字をURI内で表現する際には、まずUTF-8にエンコードし、その後にパーセントエンコーディングを行う必要があります。

# ➌ 例えば
### ❖例えば
例えば、以下のようなURIの場合を考えます。
```text
https://example.com/?text=こんにちは/file.txt
```
UTF-8の「`こ`」を16進数のコードで表すと「`0xE3 0x81 0x93`」の3バイトとなり、これにURIの規約通り「`%`」をつけて「`%E3%81%93`」と変換します。これを残りの文字列「`んにちは`」も同様に繰り返していくと以下のようになります。

```text
https://example.com/?text=%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF/file.txt
```
このように「`こんにちは`」を「`%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF`」のようにコード化することを**エンコード**と言います。またこれの逆を行うことを**デコード**と言います。

|処理名称|処理|
|:-:|:-:|
|エンコード|「`こ`」➔「`%E3%81%93`」|
|デコード|「`%E3%81%93`」➔「`こ`」|

### ❖ちなみに
> ちなみにですが、URIはエンコードされた状態で処理され、ユーザにはデコードされた状態で見えているのが基本的です。以降の**実践**で確認してみてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405376/dba5ee4e-2eb6-3c49-a04e-8fdee1f78036.png)

### ❖用語まとめ
* **エンコード（encode）**
“en”は、〜にするという意味なので、Encodeとは、「コード化する」という意味になります。

* **デコード（decode）**
“de”は、動詞の意味を反転、除去させる意味があるので、Decodeとは、「コード化されたものから元に戻す」という意味になります。

* **コード化（Encoding）とは**
コード化（Encoding）とは、情報やデータをコンピュータが理解できる形式に変換するプロセスのことです。これは、テキスト、音声、画像、動画、数値などのさまざまな形式のデータを、コンピュータが処理しやすいバイナリ形式に変換することを指します。例えば、テキストだとSJIS、UTF-8、画像だとjpg、png、音声だとmp3、wav、動画だとH.264、mp4などがあります。

# ➍ 実践
> [https://example.com/?text=こんにちは/file.txt](https://example.com/?text=こんにちは/file.txt)

それでは、上記へアクセスしてみてください。URIの日本語部分は「`こんにちは`」と表示されました。ではブラウザのURIを全部コピーして、メモ帳などへ貼り付けてみてください。
![sample](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405376/8bffe694-acea-27e0-a84a-74ed816f8c29.png)

URIの日本語部分は「`%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF`」と表示されました。
![sample](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405376/f722c9b9-efb1-e9e9-9004-1783ae26f25b.png)

ブラウザ上で表示されるURIは、日本語などの非ASCII文字が含まれていても、ユーザには見やすい形で表示されます。しかし、このURIをコピーして貼り付けると、エンコードされたURIがコピーされます。この挙動について、ユーザには見やすい形で表示されているが、内部的にはエンコードされたURIとして処理されていると考えると理解しやすいでしょう。

# ➎ 困りごと
**Teamsなどを使っている際に、Sharepoint資料を添付された場合など、どの場所のファイルなのかパスが分からなくて困ったことが多々あります**。どの場所のファイルなのかなどはURIにヒントが隠されていることが多いのですが、ブラウザ上のURIは日本語で表示されるのが基本なんですが、なぜかエンコードされたURIで表示されちゃうので、どこのファイルをいじくっているのか**チンプンカンプン**のときがあります。

そんなときにブラウザの「`エンコードURI`」から「`デコードURI`」へ変換したいと思って作ったのが、次のChrome拡張です。

# ➏ Chrome拡張
作ったChrome拡張です。
Chrome拡張ボタンを押下すると、「`上段にエンコードURI`」、「`下段にデコードURI`」を表示します。また念のための、URIコピーボタンも具備しています。
![sample](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405376/93bf0cd0-58c9-1dd5-2f08-750ab4972ae5.png)

:::note info
デコード処理については、Chrome拡張側（ローカルPC）で処理しますので、インターネット上へURIが飛ぶこともなく安全です。
:::

# ➐ ソースコード
それでは本題のソースコードです。
作るファイルは、以下の4つです。以下のファイルを全て同じフォルダに格納してください。
>1. json:manifest.json
>1. popup.html
>1. popup.js
>1. icon.png

## (1) manifest.json
`manifest.json`は、Chrome拡張機能の設定ファイルであり、拡張機能の挙動や機能、権限、アイコンなどの情報を定義します。
```json:manifest.json
{
  "manifest_version": 3,
  "name": "URI Decoder",
  "version": "1.0",
  "description": "URI Decoder.",
  "permissions": [
    "activeTab"
  ],
  "action": {
    "default_popup": "popup.html",
    "default_icon": "icon.png"
  },
  "icons": {
    "16": "icon.png",
    "48": "icon.png",
    "128": "icon.png"
  },
  "web_accessible_resources": [
    {
      "resources": [
        "icon.png"
      ],
      "matches": [
        "<all_urls>"
      ]
    }
  ]
}
```
## (2) popup.html
`popup.html`は、Chrome拡張機能のポップアップページを定義しています。
```html:popup.html
<!DOCTYPE html>
<html lang="ja">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>URL Decode</title>
  <style>
    body {
      width: 450px;
      word-wrap: break-word;
    }
  </style>
</head>

<body>
  <button id="copyButton">COPY</button>
  <span id="urlContainer"></span>
  <br>
  <button id="copyDecButton" style="display: none;">COPY</button>
  <span id="urlDecContainer" style="display: none;"></span>
  <div id="status"></div>
  <script src="popup.js"></script>
</body>

</html>
```
## (3) popup.js
`popup.js`は、Chrome拡張機能のポップアップページで使用されるJavaScriptファイルです。

:::note info
文字の符号化にはUTF-8が標準とされているのですが、**おまけ**で、URIがUTF-16（%uXXXX 形式）の場合でも、デコード対応するようにしてあります。
:::
```js:popup.js
// -----------------------------------------------------------------------------
// 「popup.html」表示時に発火する'DOMContentLoaded'イベントリスナーの設定
// -----------------------------------------------------------------------------
document.addEventListener('DOMContentLoaded', function () {

  // ページが読み込まれたら URL をデコード
  chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
    var currentTab = tabs[0];

    if (currentTab && currentTab.url) {
      var decodedUrl = decodeUnicode(currentTab.url);
      document.getElementById('urlContainer').innerText = currentTab.url;

      // 同じ場合はデコード関連のボタンとコンテナを非表示にする
      if (currentTab.url !== decodedUrl) {
        document.getElementById('copyDecButton').style.display = 'inline';
        document.getElementById('urlDecContainer').style.display = 'inline';
        document.getElementById('urlDecContainer').innerText = decodedUrl;
      }
    } else {
      console.error("無効なタブまたはURLプロパティが見つかりません.");
    }
  });

  // 「COPY」ボタン押下時に発火する'click'イベントリスナーの設定
  document.getElementById('copyButton').addEventListener('click', function () {
    var urlText = document.getElementById('urlContainer').innerText;
    copyToClipboard(urlText);
  });

  // 「COPY」ボタン押下時に発火する'click'イベントリスナーの設定
  document.getElementById('copyDecButton').addEventListener('click', function () {
    var urlText = document.getElementById('urlDecContainer').innerText;
    copyToClipboard(urlText);
  });
});

// -----------------------------------------------------------------------------
// URLをデコードする関数
// -----------------------------------------------------------------------------
function decodeUnicode(url) {

  try {
    url = decodeURIComponent(url);
    // UTF-16（%uXXXX 形式）を通常の URL エンコードに変換してデコード
    url = url.replace(/%u([0-9A-Fa-f]{4})/g, function (match, hex) {
      return String.fromCharCode(parseInt(hex, 16));
    });
  } catch (err) {
    // UTF-16（%uXXXX 形式）を通常の URL エンコードに変換してデコード
    url = url.replace(/%u([0-9A-Fa-f]{4})/g, function (match, hex) {
      return String.fromCharCode(parseInt(hex, 16));
    });
  }

  try {
    url = decodeURIComponent(url);
  } catch (err) {
    console.error("decodeURIComponent ERROR:", url, err);
  }
  return url;
}

// -----------------------------------------------------------------------------
// クリップボードへテキストをコピーする
// -----------------------------------------------------------------------------
async function copyToClipboard(text) {

  try {
    // クリップボードへコピーチャレンジ
    await navigator.clipboard.writeText(text);

    // クリップボードへコピーチャレンジ成功
    const successMessage = 'クリップボードへのコピーが成功しました.';
    console.log(successMessage + ':', text);
    document.getElementById('status').innerText = successMessage;

  } catch (err) {

    // クリップボードへコピーチャレンジ失敗
    const errorMessage = 'クリップボードへのコピーが失敗しました.';
    console.error(errorMessage + ':', err);
    document.getElementById('status').innerText = errorMessage;
  }
}
```
## (4) icon.png
著作権フリーのもので、デコードとかコピーとかそれっぽい画像を見つけて「`icon.png`」というファイル名に変更しておいてください。

# ➑ Chrome拡張追加方法
1. Chromeを立ち上げ、右上の3点リーダ「`︙`」から「`拡張機能`➔`拡張機能を管理`」を選択してください
![sample](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405376/a4d240f9-8706-ac36-2b62-962295744211.png)

1. 拡張機能画面が立ち上がったら、右上の「`デベロッパーモード`」を「`ON`」にしてください

1. 「`パッケージ化されていない拡張機能を読み込む`」を押下して、➐で作ったプログラムのフォルダを指定してください
![sample](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405376/7e1d1c34-83f1-f26a-3024-9b09fa223149.png)

1. パズルマーク「🧩」みたいなChrome拡張機能ボタンを押下して、「`URI Decoder`」をピンドメ「📌」しておいてください
![sample![uploading...0]()
](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405376/3f3c0293-85a0-029e-e8c5-7d303e6fcfc2.png)

1. ツールバーに「`URI Decoder`」が表示されたらインストール完了です
![sample](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405376/e26c545e-c9e6-edc0-4e6e-6256bd9c5c44.png)

# ➒ Chrome拡張の使い方
Chrome拡張の使い方ですが、普通にブラウジングしてください。デコードしたいURIのときに「`URI Decoder`」を押下してメニューを表示し、必要があれば「`COPY`」ボタンを押下してクリップボードへコピーしてください。
![sample](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405376/c2fd8f78-2f88-34d2-555c-410d839b7e61.png)

* 上段：`URIエンコード`の表示（クリップボードへのコピー機能）
* 下段：`URIデコード`の表示（クリップボードへのコピー機能）

# ➓ さいごに🫡
[Chrome拡張は以前もやった](https://qiita.com/PoodleMaster/items/77077ebc80c6f16eb4db)のですが、忘れてきたのでなんか作ってみたいなと思っていたのと、調度良い課題（欲望）があったので、トライしてみました。今回は、Chrome拡張の作成方法の再確認、URIのエンコード/デコードの簡単な規約確認、javascriptプログラムなどのお勉強ができました。**直接的に要求仕様が合致する人は少ないと思いますが、Chrome拡張を作ってみたいという方には、簡単なソースコードなので参考になるかもと思いQiitaに出力しておきます**。また課題を見つけてChrome拡張作ってみたいと思います！

# ⓫ ボヤキ😶‍🌫️
本記事とはあまり関係ないんですが、Pythonでツールを作っては生産性向上をバリバリしているんですが、会社はあまり評価してくれないですよね…💩💩💩どうしたものか…
以上ボヤキでした。