トリビア: location.port = undefined すると port が 0 になるのなんでだろう

https://twitter.com/ninjinkun/status/1059300775413743616

<blockquote class="twitter-tweet" lang="ja"><p>window.location.port = undefined するとportが0になるのなんでだろう</p>&mdash; にんじんくん (@ninjinkun) <a href="https://twitter.com/ninjinkun/status/1059300775413743616">2018, 11月 4</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

自分も気になったので調べてみた。

結論を先に書くと、どうやら **共通の仕様はないがブラウザ同士の後方互換性によってこのような暗黙のルールになっている** ということっぽい。

## まずは実際にやってみた

要素を検証 (Inspect Element) でコンソールを開き、 `location.port = undefined` と代入してみる。

<img width="597" alt="fig0.png" src="https://qiita-image-store.s3.amazonaws.com/0/25886/2d1e28cc-833e-91ff-ea5d-26055fc9ad43.png">
<img width="597" alt="fig1.png" src="https://qiita-image-store.s3.amazonaws.com/0/25886/0aa06ed3-5d2e-e245-906d-193c2a7f9fd6.png">
<img width="597" alt="fig2.png" src="https://qiita-image-store.s3.amazonaws.com/0/25886/4c923a97-e58e-9fd7-ba8a-5dad9b3e6e40.png">


## どのブラウザで発生するか

手元にあった

- Google Chrome (Version 70.0.3538.102 (Official Build) (64-bit)
- Firefox Quantum (63.0.3 (64-bit))
- Safari Version 12.0.1 (14606.2.104.1.1)

ではそのような動作になった。

## undefined 以外ではどうか

- `location.port = 'Hello, World!'`


### `:0` になったもの

- `location.port = null`
- `location.port = 'Hello, World!'`
- `location.port = alert`
- `location.port = 'Hello, World!'`

どうやら数値として解釈できない場合には `:0` になるっぽい。

## 明文化された仕様にはないっぽい

- WHATWG の Living Standard には、 [Host parsing](https://url.spec.whatwg.org/#host-parsing) についての記述はあるものの、 port については「解釈できない場合は 0 とする」ようなルールは見つけられなかった。

- W3C の [Uniform Resource Locators (URL) A Syntax for the Expression of Access Information of Objects on the Network](https://www.w3.org/Addressing/URL/url-spec.txt) にも見つけられなかった。

- MDN の URL https://developer.mozilla.org/ja/docs/Web/API/URL
- MDN の Location https://developer.mozilla.org/ja/docs/Web/API/Location

にもみつけられなかった。
どこかにはあるかもしれないが、少なくとも kitsuyui にはみつけられなかった。

## ソースコードを追う

しかたがないので Chromium のソースコードを追うと `ParsePortFromStringPosition` という箇所に該当の処理があった。 https://github.com/chromium/chromium/blob/146802d392d308b084a472f363da971be7956e3c/third_party/blink/renderer/platform/weborigin/kurl.cc#L466-L474

```C
while (IsASCIIDigit(value[port_end]) && port_end < length)
  ++port_end;
while (value[port_start] == '0' && port_start < port_end - 1)
  ++port_start;

// Required for backwards compat.
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=23463
if (port_start == port_end)
  return "0";
```

コメントにあったチケット https://www.w3.org/Bugs/Public/show_bug.cgi?id=23463 を追ってみると、今回の問いと同様の質問をしているユーザがいる。 Webkit / Blank ともに Gecko (つまり Firefox) に合わせているのでこうなっている、という回答が見つかる。

## では Firefox はいつからこのような挙動なのか ?

Firefox のソースも追ってみる。

- これが最新版のコード https://github.com/mozilla/gecko-dev/blob/b48139f5dce4c63100c846127b752e8f1225ef5c/dom/base/Location.cpp#L638-L645

これをひたすら遡って、いつからそうなっているのかみてみよう。

(途中でリファクタによってファイル名が変わっているので、 GitHub 上で遡るのはちょっと苦労するかもしれない。 git clone してコマンドで探す方が楽だろう。)

https://github.com/mozilla/gecko-dev/blame/50187fd7d034ca79bf8d51c0b5014140a3e69b73/dom/src/base/nsLocation.cpp#L381-L388

日付をみると 1998 年。なのでおそらく Netscape がオープンソース化された当初にはこのような挙動だったらしい。

C の標準ライブラリ関数 [`atol`](https://ja.wikipedia.org/wiki/Atol) でシンプルに変換しているだけなので、変換に失敗しても `0` を返す (というよりも `errorno` を更新しないし失敗という概念が `atol` にはない) 、ということになっていたわけだ。実に単純な話だった。

これが現代でも暗黙の仕様として各種のブラウザに実装されているというわけだ。

## 他の太古のブラウザでは……？

他の旧いブラウザの挙動もみてみたかったが、手元には環境がない。
同じ時代の Internet Explorer ではどのような挙動になるか、などなど興味は尽きない。

残念ながら手元に 1998 年の Internet Explorer がないのでここで調査は打ち切りにする。