Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 14186] ファイルの概要

このコミットは、Go言語プロジェクトのChrome拡張機能であるgophertool内のリンクが正しく機能しない問題を修正するものです。具体的には、HTML要素のonclick属性を直接使用してURLを開く代わりに、カスタムのurl属性とJavaScriptのaddEventListenerを組み合わせてリンク処理を行うように変更しています。これにより、より堅牢で保守性の高いリンク管理が実現されています。

コミット

commit 3d00648dc1b08873f7e779d5a842641795ec42ad
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Mon Oct 22 01:05:21 2012 +0800

    gophertool: fix links
    
    R=bradfitz
    CC=golang-dev
    https://golang.org/cl/6713043

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/3d00648dc1b08873f7e779d5a842641795ec42ad

元コミット内容

gophertool: fix links

R=bradfitz
CC=golang-dev
https://golang.org/cl/6713043

変更の背景

このコミットの背景には、Go言語プロジェクトのChrome拡張機能であるgophertoolにおけるリンクの動作不良がありました。元の実装では、HTMLの<a>タグに直接onclick属性を記述し、JavaScript関数openURLを呼び出して新しいタブでURLを開くという方式が取られていました。しかし、この方式はいくつかの問題を引き起こす可能性がありました。

  1. 保守性の問題: HTMLとJavaScriptのロジックが密結合になり、変更やデバッグが困難になる。
  2. イベントハンドリングの制限: onclick属性は単一のイベントハンドラしか設定できず、複数のスクリプトが同じ要素のクリックイベントを処理しようとすると競合が発生する可能性がある。
  3. セキュリティとCSP (Content Security Policy): Chrome拡張機能では、インラインJavaScript(HTML内に直接記述されたJavaScript)がCSPによって制限されることがあり、onclick属性のようなインラインイベントハンドラが機能しない、または非推奨となる場合があります。

これらの問題を解決し、より堅牢でモダンな方法でリンクを処理するために、この変更が導入されました。

前提知識の解説

gophertool

gophertoolは、Go言語プロジェクトに関連するChrome拡張機能です。Goの公式Gitリポジトリ内のmisc/chrome/gophertoolディレクトリに存在し、JavaScript、HTML、CSSで記述されています。Go関連の情報を素早く参照するためのシンプルなツールとして機能していたと考えられます。

Chrome拡張機能

Chrome拡張機能は、Google Chromeブラウザの機能を拡張するための小さなソフトウェアプログラムです。HTML、CSS、JavaScriptといったウェブ標準技術を使用して開発され、ブラウザのUIをカスタマイズしたり、ウェブページと対話したり、特定のタスクを自動化したりすることができます。拡張機能は、セキュリティ上の理由から、通常のウェブページとは異なる権限モデルと実行環境を持っています。

onclick属性とaddEventListener

ウェブ開発において、ユーザーの操作(クリックなど)に応答するためにイベントハンドラを設定する方法はいくつかあります。

  • onclick属性: HTML要素の属性として直接JavaScriptコードを記述する方法です。

    <button onclick="alert('Hello!');">Click me</button>
    

    これはシンプルですが、HTMLとJavaScriptが混在し、保守性が低下する傾向があります。また、一つの要素に対して複数のonclickハンドラを設定すると、後から設定されたものが以前のものを上書きしてしまいます。

  • addEventListener: JavaScriptのDOM (Document Object Model) APIの一部であり、要素にイベントリスナーを動的にアタッチするための推奨される方法です。

    document.getElementById('myButton').addEventListener('click', function() {
        alert('Hello!');
    });
    

    この方法は、HTMLとJavaScriptの分離を促進し、一つの要素に複数のイベントハンドラを登録できる柔軟性を提供します。また、イベントの伝播フェーズ(キャプチャリングフェーズとバブリングフェーズ)を制御するオプションも提供します。Chrome拡張機能の開発においては、セキュリティと保守性の観点からaddEventListenerの使用が強く推奨されます。

code.google.com/p/go

code.google.com/p/goは、かつてGo言語プロジェクトの公式リポジトリとして使用されていたGoogle Codeプラットフォーム上のURLです。Google Codeは2016年に閉鎖されており、現在はGo言語の公式ウェブサイトはgo.dev、メインのGitリポジトリはgo.googlesource.com/goに移行しています。GitHubにもミラーリポジトリが存在します。

codereview.appspot.com

codereview.appspot.comは、RietveldというオープンソースのコードレビューツールのURLでした。これはPythonの作者であるGuido van Rossumによって作成され、Google App Engine上で動作していました。Goチームも一時期、このRietveldを使用してコードレビューを行っていました。しかし、Rietveldも現在はメンテナンスされておらず、Goプロジェクトは別のコードレビューシステム(Gerritベースのgolang.org/cl)に移行しています。

golang.org/cl

golang.org/clは、Go言語プロジェクトが使用しているGerritベースのコードレビューシステムです。Goプロジェクトへの変更は、まずこのシステムに「変更リスト (Change List, CL)」として提出され、レビューと承認を経てメインリポジトリにマージされます。コミットメッセージに含まれるhttps://golang.org/cl/6713043は、このコミットがどのコードレビューに対応しているかを示しています。ただし、検索結果によると、この特定のCLはgo listコマンドに-jsonフラグを追加する別の変更に関するものであり、今回のgophertoolの変更とは直接関係がないようです。これは、コミットメッセージのテンプレートや慣習によるものか、あるいは関連する別の変更がこのCLで議論された可能性が考えられます。

技術的詳細

このコミットの技術的な核心は、HTML要素のイベントハンドリングのパラダイムシフトにあります。

元のコードでは、以下のような形式でリンクが記述されていました。

<a href="#" onclick="openURL('http://code.google.com/p/go/issues/list')">issue</a>

ここで問題となるのは、onclick属性に直接JavaScriptコードが記述されている点です。これはインラインJavaScriptと呼ばれ、前述の通り保守性やセキュリティの観点から推奨されません。特にChrome拡張機能では、Content Security Policy (CSP) によってインラインJavaScriptがデフォルトで禁止されているため、この形式のコードは実行されないか、警告を発する可能性があります。

新しいアプローチでは、以下の変更が行われました。

  1. カスタム属性urlの導入: <a>タグにurlというカスタム属性が追加され、そこに開きたいURLが格納されます。

    <a href="#" url="http://code.google.com/p/go/issues/list">issue</a>
    

    href="#"は、リンクがクリックされたときにページがリロードされるのを防ぐための一般的な慣習です。

  2. JavaScriptによるイベントリスナーの動的アタッチ: popup.js内で、すべての<a>タグを走査し、url属性を持つものに対してclickイベントリスナーを動的に追加するaddLinks関数が導入されました。

    function addLinks() {
      var links = document.getElementsByTagName("a");
      for (var i = 0; i < links.length; i++) {
        var url = links[i].getAttribute("url");
        if (url)
          links[i].addEventListener("click", function () {
            openURL(this.getAttribute("url"));
          });
      }
    }
    

    このaddLinks関数は、ページの読み込みが完了した後にwindow.addEventListener("load", ...)内で呼び出されます。

この変更により、HTMLは純粋に構造とデータ(URL)を記述する役割に徹し、JavaScriptがイベントハンドリングのロジックを完全に担当するようになりました。これにより、コードの分離が図られ、可読性、保守性、そしてChrome拡張機能のCSP要件への適合性が向上します。

また、以前はbuildbotsリンクに対してのみ、window.addEventListener("click", ...)でグローバルなクリックイベントリスナーを設定し、特定のURLを開くという特殊な処理が行われていましたが、これも新しい汎用的なaddLinks関数によって統一的に処理されるようになりました。これにより、コードの重複が排除され、一貫性が保たれています。

コアとなるコードの変更箇所

diff --git a/misc/chrome/gophertool/popup.html b/misc/chrome/gophertool/popup.html
index aec10048a3..8bb7795fac 100644
--- a/misc/chrome/gophertool/popup.html
+++ b/misc/chrome/gophertool/popup.html
@@ -9,11 +9,11 @@
 <script src="popup.js"></script>
 </head>
 <body style='margin: 0.5em; font-family: sans;'>
-<small><a href="#" onclick="openURL('http://code.google.com/p/go/issues/list')">issue</a>,
-<a href="#" onclick="openURL('http://codereview.appspot.com/')">codereview</a>,
-<a href="#" onclick="openURL('http://code.google.com/p/go/source/list')">commit</a>, or
-<a href="#" onclick="openURL('http://golang.org/pkg/')">pkg</a> id/name:</small>
+<small><a href="#" url="http://code.google.com/p/go/issues/list">issue</a>,
+<a href="#" url="http://codereview.appspot.com/">codereview</a>,
+<a href="#" url="http://code.google.com/p/go/source/list">commit</a>, or
+<a href="#" url="http://golang.org/pkg/">pkg</a> id/name:</small>
 <form style='margin: 0' id='navform'><nobr><input id="inputbox" size=10 tabindex=1 /><input type="submit" value="go" /></nobr></form>
-<small>Also: <a href="#" id='buildbotslink'>buildbots</small>
+<small>Also: <a href="#" url="http://build.golang.org">buildbots</a></small>
 </body>
 </html>
diff --git a/misc/chrome/gophertool/popup.js b/misc/chrome/gophertool/popup.js
index 717fc169cd..410d65120e 100644
--- a/misc/chrome/gophertool/popup.js
+++ b/misc/chrome/gophertool/popup.js
@@ -2,7 +2,19 @@ function openURL(url) {
   chrome.tabs.create({ "url": url })
 }
 
+function addLinks() {
+  var links = document.getElementsByTagName("a");
+  for (var i = 0; i < links.length; i++) {
+    var url = links[i].getAttribute("url");
+    if (url)
+      links[i].addEventListener("click", function () {
+        openURL(this.getAttribute("url"));
+      });
+  }
+}
+
 window.addEventListener("load", function () {
+  addLinks();
   console.log("hacking gopher pop-up loaded.");
   document.getElementById("inputbox").focus();
 });
@@ -32,7 +44,3 @@ window.addEventListener("submit", function () {\n   console.log("no match for text: " + t)\n   return false;\n });\n-\n-window.addEventListener("click", function () {\n-  openURL("http://build.golang.org/");\n-});\n```

## コアとなるコードの解説

### `misc/chrome/gophertool/popup.html` の変更

*   **`- <a href="#" onclick="openURL('...')">...</a>`**: 既存のリンク要素から`onclick`属性が削除されています。
*   **`+ <a href="#" url="...">...</a>`**: 新しく`url`というカスタム属性が追加され、そこに開きたいURLが直接記述されています。これにより、HTMLはデータの保持に専念し、JavaScriptのロジックから分離されます。
*   `buildbots`リンクも同様に、`id='buildbotslink'`が削除され、`url="http://build.golang.org"`が追加されています。これにより、すべてのリンクが同じパターンで処理されるようになります。

### `misc/chrome/gophertool/popup.js` の変更

*   **`function openURL(url)`**: この関数は変更されていません。与えられたURLを新しいChromeタブで開く役割を担います。
*   **`+ function addLinks() { ... }`**: 新しく`addLinks`関数が追加されました。
    *   `var links = document.getElementsByTagName("a");`: ドキュメント内のすべての`<a>`タグ(アンカー要素)を取得します。
    *   `for (var i = 0; i < links.length; i++) { ... }`: 取得したすべてのリンク要素をループで処理します。
    *   `var url = links[i].getAttribute("url");`: 各リンク要素から`url`カスタム属性の値を取得します。
    *   `if (url)`: `url`属性が存在する場合のみ処理を続行します。
    *   `links[i].addEventListener("click", function () { openURL(this.getAttribute("url")); });`: 取得した`url`属性を持つリンク要素に対して`click`イベントリスナーを追加します。
        *   `this.getAttribute("url")`: イベントが発生した要素(クリックされた`<a>`タグ)の`url`属性の値を取得し、それを`openURL`関数に渡します。これにより、正しいURLが新しいタブで開かれます。
*   **`window.addEventListener("load", function () { ... });`**: ページの読み込みが完了したときに実行されるイベントリスナーです。
    *   **`+ addLinks();`**: この行が追加され、ページの読み込み完了後に`addLinks`関数が呼び出されるようになりました。これにより、すべてのリンクに動的にイベントリスナーがアタッチされます。
*   **`- window.addEventListener("click", function () { openURL("http://build.golang.org/"); });`**: 以前の`buildbots`リンク専用のグローバルなクリックイベントリスナーが削除されました。これは`addLinks`関数によって汎用的に処理されるようになったため、不要になりました。

これらの変更により、`gophertool`のリンク処理は、よりモダンで堅牢なJavaScriptのベストプラクティスに沿ったものになりました。

## 関連リンク

*   Go言語公式サイト: [https://go.dev/](https://go.dev/)
*   Go言語のGitHubリポジトリ: [https://github.com/golang/go](https://github.com/golang/go)
*   Chrome拡張機能の概要: [https://developer.chrome.com/docs/extensions/mv3/overview/](https://developer.chrome.com/docs/extensions/mv3/overview/)
*   `EventTarget.addEventListener()` - Web API | MDN: [https://developer.mozilla.org/ja/docs/Web/API/EventTarget/addEventListener](https://developer.mozilla.org/ja/docs/Web/API/EventTarget/addEventListener)

## 参考にした情報源リンク

*   Web search results for "gophertool chrome extension golang"
*   Web search results for "chrome extension link handling best practices onclick addEventListener"
*   Web search results for "golang.org/cl/6713043"
*   Web search results for "code.google.com/p/go"
*   Web search results for "codereview.appspot.com"