rch850 の上澄み

技術的な話題とか、雑談とか。タイトルを上澄みに変えました @ 2020/09/02

Chrome 59 で window.open の挙動が変わった

JavaScript で新しいウィンドウを開くため、このようなコードを書いていたのですが、Chrome 59 になってから新しいタブで開くようになってしまいました。

window.open('http://example.com/', '_blank', 'width=640, height=480, location=yes')

window.open の第3引数 feature から、アドレスバーを表示するためのオプション location=yes を取り除くことで、タブではなくウィンドウで開くようになりました。

window.open('http://example.com/', '_blank', 'width=640, height=480')

こうなった原因を探るため Chrome 59 のコミットログwindow.open で検索したところ window.open() should gate new tab/new popup based on toolbar visibility. (e507bb3) が関係してそうでした。

window.open() should gate new tab/new popup based on toolbar visibility.

Previously, Chrome required that toolbar, menubar, scrollbars, status, resizable were all set to enabled to open a window as a new tab rather than a new popup. However, this causes developer frustration if one of window features is accidentally omitted (as it then defaults to disabled).

Instead, just use toolbar visibility to determine whether or not window.open() creates a new popup or a new tab, which matches Firefox.

なるほど。toolbar についても調べたところ、確かに設定次第でポップアップかどうかが変わりました。

// 新しいタブになる
window.open('http://example.com/', '_blank', 'width=640, height=480, toolbar=yes')

// 新しいウィンドウ(ポップアップ)になる
window.open('http://example.com/', '_blank', 'width=640, height=480, toolbar=no')
window.open('http://example.com/', '_blank', 'width=640, height=480')

Chrome 59 のソースを調べたことのメモ

どこかに location を toolbar として見るコードがあるはずですが、見つけられませんでした。


(6月20日追記)

location=yes の有無でどうなるか、IE 11, Edge, 14, Chrome 59, Firefox 54 で動作確認しました。

codepen.io

結果はリンク先を見ての通りですが、特に挙動が違ったところとして IE 11 だけ location=yes の時にアドレスバーの中身を編集できました。

oEmbed の height null について

きっかけは mastodon の URL 貼り付けを確認してたときにびろーんと伸びてしまうのに気づいたこと。

friends.nico

引用ここまで。めっちゃ改行入れてるわけじゃなくて、ここまでびろーんって伸びちゃってるんです。

逆に、長いトゥートは途切れてしまう。

pawoo.net

なぜこうなるか mastodon のコードを追ってみた。対象はタグ v1.3.2 のもの。

oEmbed の実装は oembed_controller.rb にあって、特別な指定がなければ height が 640 となる。

  def show
    @stream_entry = stream_entry_from_url(params[:url])
    @width        = params[:maxwidth].present?  ? params[:maxwidth].to_i  : 400
    @height       = params[:maxheight].present? ? params[:maxheight].to_i : 600
  end

https://github.com/tootsuite/mastodon/blob/v1.3.2/app/controllers/api/oembed_controller.rb#L9

height というのは oEmbed の仕様にあって rich タイプでは必須となっている。

height (required)

The height in pixels required to display the HTML.

この高さが 640 と固定されているので、びろーんと伸びてしまったり、途切れてしまったりするわけだ。

Twitter がどうしているか調べてみたら、height には null が入っていた。instagram も同様に null だった。

じゃぁ mastodon でも null 返せばいいのか?ってことで、自分の mastodon インスタンスで show メソッドを書き換えて null になるようにしたら、いい感じにフィットするようになった。

mastodon.850mb.net

なるにはなったけど required って明記されているものに対して null を返すのはちょっと抵抗あるなー。本家に PR 出しても苦い顔されそうだし、自分がその立場なら苦い顔しそう。

なお今回の調査では iframely のデバッガにとてもお世話になりました。

(5/9 追記) 出すだけ出してみようってことで PR 出したらすぐマージされましたとさ。ウワサには聞いてたけど対応速かった!

github.com

トゥート!の埋め込み

f:id:rch850:20170421003214p:plain

oEmbed 対応してるみたい。

f:id:rch850:20170421003609p:plain

なんか不格好なのは iframe の height が 600 もあるからかな。

$ curl "https://friends.nico/api/oembed.json?url=https%3A%2F%2Ffriends.nico%2Fusers%2Frch850%2Fupdates%2F48866"
{"type":"rich","version":"1.0","title":"New status by rch850","author_name":"りちゃ","author_url":"https://friends.nico/users/rch850","provider_name":"friends.nico","provider_url":"https://friends.nico/","cache_age":86400,"html":"<iframe src=\"https://friends.nico/users/rch850/updates/48866/embed\" style=\"width: 100%; overflow: hidden\" frameborder=\"0\" width=\"400\" height=\"600\" scrolling=\"no\"></iframe>","width":400,"height":600}

コミットメッセージの Emoji Prefix の虫をやめた話

git とかのコミットメッセージで 👍 ほげを実装しました とか 🆙 ほげパッケージを 1.2.3 にアップデート みたいに書くのあるじゃないですか。

memo.goodpatch.co

好きで結構使ってるんですが、バグの絵文字だけは虫を使ってません。

虫の絵文字って、だいたいは芋虫みたいなやつだと思うんですが、こいつが Gmail に入ってくるとあいつに化けるんです。

f:id:rch850:20170408122603p:plain:w200

大変不愉快なのでモザイクをかけてます。気になる方はご自分の Gmail アドレス宛に虫の絵文字を送りつけてみてください。あと Emojipedia にもあったので、勇気のある方はこちらからどうぞ。拡大してみると Google 以外もなかなか強烈……

虫をやめてどうしたかというと、救急車にしました。

f:id:rch850:20170408122927p:plain:w200

個人的には Gmail 上での虫の表現がぞわぞわしたので、それが無くなってよかったのですが、「実は虫自体が苦手でした」って人もいて、この変更は好評でした。

Happy git life!!

ふくもく会その26

午前中はウェアラブル/VRセミナー&アイデアソンで進行などをして、お昼食べに行って、午後2時半からの参加でした。

何やるか全然決めてなかったんで、若干の圧を発してたら、やること共有の発表順がラストになりました。やったぜ。Python 画像処理の勉強しますとか、webpack プラグインのメンテしますという話を聞いてたら、Python のパッケージ公開でもやってみるかという気分になりました。npm も gem も公開したことはあるんですが、Python はやったことなかったので。

りちゃ on Twitter: "できあがったもの ahg48 0.1.3 : Python Package Index https://t.co/YXWE4492DM #ふくもく会"

Packaging and Distributing Projects がしっかり書いてあるので、これに従えばほぼ大丈夫かと。バージョンが 0.1.3 になってるのは、最初に野良記事を参考にやって long_description を README から読み出すのに失敗したりしていたからです。今後は CI かますなどしようかなと思います。

他には @kotobuki555ing中京テレビハッカソンの報告を聞いたりしてました。コンスタントに受賞しててすごい。ハッカソン中やアイデアソンとハッカソンの間の平日に、たくさんインタビューがあったそうです。テレビ局すごい。

次回はたぶん4月29日です。

MediaRecorder 後日談

rch850.hatenablog.com

の続き。

Angular2 で audio タグの src に blob:http://localhost:4200/8faa4fbf-a787-4171-9e2a-703e4d89c328 みたいなのを設定しようとすると、

WARNING: sanitizing unsafe URL value blob:http://localhost:4200/dbce8851-3f4b-4509-87f3-5116f0cbadb4 (see http://g.co/ng/security#xss)

みたいのが出たわけです。あ、これ AngularJS 1.x でも見たやつだ。ってことで、unsafe の解除方法調べて、DomSanitizer を使えばいいんだなってことが分かりました。

それで無事に sanitizer.bypassSecurityTrustUrl(blobUrl) みたいにして src に設定できるようになったんですが、それでもなぜかエラーが出る。

GET blob:http://localhost:4200/0be05bbb-5fc6-4bb8-ac93-d29096be6aea 416 (Requested Range Not Satisfiable)

これについてはだいぶ調べたんですが、まったく解決策が見つからず。しばらく経ってから、これそもそも動くの?って疑問が出てきて、MDN が提供してるサンプルを動かしてみたら……

f:id:rch850:20170215233131p:plain

んーーーーーーー

疲れた。


(2/21)さらに後日談。サンプルを再度動かしたらちゃんと動きました。ブラウザの機嫌が悪かったのかも。動いてよかった。

Web Audio API の奥底から Angular とつながりたかった

Angular 2 の勉強がてらに、Web Audio API で録音するウェブアプリを作ろうとしたら、変なところでハマったのでメモ。

録音には MediaRecorder を使おうとしました。コードは今のリンク先にあるものを参考に書きました。getUserMedia で取った stream を使って MediaRecorder を作って、その onstop で結果を得る形です。

が、なかなか録音完了後の結果がビューに反映されませんでした。AngularJS 1.x で言うダイジェストをしたかったんですが、ドキュメント の Change Detection あたりを読んでも Angular zone がいい具合にやるから大丈夫と書いてあるだけでさっぱり。

いろいろ調べて Triggering Angular2 change detection manually - Stack Overflow に行き当たって ChangeDetectorRef.detectChanges() が良さそうということが分かりました。

最終的にはこのようなコードになりました。

  constructor(private ref: ChangeDetectorRef) {
    navigator.mediaDevices.getUserMedia(
      { audio: true }
    ).then((stream: MediaStream) => {
      this.mediaRecorder = new MediaRecorder(stream);
      this.mediaRecorder.ondataavailable = (e) => {
        this.chunks.push(e.data)
      }
      this.mediaRecorder.onstop = (e) => {
        let clipName = prompt("Enter a name for your sound clip")

        let blob = new Blob(this.chunks, {"type": "audio/ogg; codecs=opus"})
        this.chunks = []
        this.clips.push({
          name: clipName,
          audioUrl: window.URL.createObjectURL(blob)
        })
        console.log(this.clips)
        // これを呼び出したら clips への要素追加が反映された
        this.ref.detectChanges()
      }
    }, function(err: any) {
      console.error(err);
    });
  }

以上!