LGTM

Looks Good To Me

Tumblr Dashboard でChrome + AutoPagerize を動かす

普段ChromeAutoPagerize 入れて楽しんでますが,Tumblr Dashboard ではうまく動きません.

AutoPagerize の次ページロード方法を変えれば動くっぽいので,やってみました.

2014/05/22 追記 Chrome 35.0.1916.114 に対応

動かないのはなぜ?

そのままだと次のようなエラーで止まります.

Refused to display 'http://www.tumblr.com/dashboard/x/xxxxxxxxxxx' in a frame because it set 'X-Frame-Options' to 'deny'.

AutoPagerize

  1. <iframe/> を新たに作成
  2. そこに次ページをロード
  3. 次ページのDOMを元のページに追加
  4. 作った<iframe/> を削除

ということをしているようで,X-Frame-Options:deny を返すWebサイトでは当然ながら動きません.

昔は<iframe/> ではなくXHR で次ページをロードしていたようなんですが...

なぜ <iframe/> ?

次ページがクロスオリジンなサイトだった場合に動きを止めたいが,XHR だとうまく実装できない

というのが理由だそうです.

Work with X-Frame-Options:deny on Chrome #3

  • req.responseType = 'document' を指定してresponse.URL を検証すればクロスオリジンかどうか判定できるが,イマイチ
    • PR時点のChrome ではresponse.URL に望ましい値が入っていないため,信用できなかった
  • 当時のChrome では,なぜかクロスオリジンへリダイレクトできなかった
    • 動作としては望ましいが...
    • 理由がはっきりしないし将来動作が変わる恐れがあるため,これに依存したくない

ですって.

XHR 版をつかってみる

ここからが本題.

Work with X-Frame-Options:deny on Chrome #3

によると,<iframe/> の代わりにXHR で実装したブランチを作ってくれていました.そっちを試してみます.

X-Frame-Options によらず,動いてくれる期待が膨らみますね!

やることは

  1. ソースコードを取ってきて,最新版とマージする
  2. chrome.webRequest パッチを適用 (2014/05/22 追記)
  3. .crx をビルドする
  4. Chrome に入れる

です.

1. ソースコードを取ってきて,最新版とマージする

github 上のコードは最新版ではないため,XHR 版ブランチを最新のものにします.

現時点での最新版は0.3.7.

わしのリブログ環境について にもマージ済みのfork がありましたが,メッセージファイルなどの差分が取り込まれてなくて...いちおう手元でやっときました.

(取り込まれてないと何が困るか確かめてない)

git clone https://github.com/swdyh/autopagerize_for_chrome.git
cd autopagerize_for_chrome

# 最新版コードで上書き (OSX の場合)
cp -r ~/Library/Application\ Support/Google/Chrome/Default/Extensions/igiofjhpmpihnifddepnpngfjhkfenbp/0.3.7_0/* src

git add .
git commit -m "Merged 0.3.7 which was officially released"

git checkout origin/xhr_with_webRequest -b xhr_with_webRequest
git rebase master
Auto-merging src/manifest.json
CONFLICT (content): Merge conflict in src/manifest.json
Auto-merging src/background.js
CONFLICT (content): Merge conflict in src/background.js
Auto-merging src/autopagerize.user.js
Automatic merge failed; fix conflicts and then commit the result.

やはりconflict しますね.

(ちょっと長いですが git diff 掲載します)

diff --cc src/background.js
index 7579f4b,80132e5..0000000
--- a/src/background.js
+++ b/src/background.js
@@@ -157,3 -159,39 +157,42 @@@ function get(url, callback, opt) 
      xhr.send(null)
      return xhr
  }
++<<<<<<< HEAD
++=======
+ 
+ // Add X-XMLHttpRequest-Final-URL
+ var redirects = {}
+ chrome.webRequest.onBeforeRedirect.addListener(
+     function (details) {
+         redirects[details.requestId] = details.redirectUrl
+     },
+     { urls: ["<all_urls>"], types: ["xmlhttprequest"] }
+ )
+ chrome.webRequest.onHeadersReceived.addListener(
+     function(details) {
+         var name = 'X-XMLHttpRequest-Final-URL'
+         var value = (redirects[details.requestId] || details.url)
+         var add = true
+         for (var i = 0; i < details.responseHeaders.length; i++) {
+             if (details.responseHeaders[i].name === name) {
+                 details.responseHeaders[i].value = value
+                 add = false
+             }
+         }
+         if (add) {
+             details.responseHeaders.push({ name: name, value: value })
+         }
+         return { responseHeaders: details.responseHeaders }
+     },
+     { urls: ["<all_urls>"], types: ["xmlhttprequest"] },
+     ["blocking", "responseHeaders"]
+ )
+ chrome.webRequest.onCompleted.addListener(
+     function (details) {
+         if (redirects[details.requestId]) {
+             delete redirects[details.requestId]
+         }
+     },
+     { urls: ["<all_urls>"], types: ["xmlhttprequest"] }
+ )
++>>>>>>> use xhr with webRequest
diff --cc src/manifest.json
index 8c24254,e11b29d..0000000
--- a/src/manifest.json
+++ b/src/manifest.json
@@@ -1,32 -1,48 +1,68 @@@
  {
 -  "manifest_version": 2,
 -  "homepage_url": "http://autopagerize.net/",
 -  "content_scripts": [
 -    {
 -      "js": [
 -        "extension.js",
 -        "autopagerize.user.js"
 -      ],
 -      "matches": [
 -        "http://*/*",
 -        "https://*/*"
 -      ],
 +   "background": {
 +      "page": "background.html"
 +   },
 +   "content_scripts": [ {
 +      "js": [ "extension.js", "autopagerize.user.js" ],
 +      "matches": [ "http://*/*", "https://*/*" ],
        "run_at": "document_end"
++<<<<<<< HEAD
 +   } ],
 +   "default_locale": "en",
 +   "description": "A browser Extension for auto loading paginated web pages. AutoPagerize use in many web site, and provide efficiently web browsing.",
 +   "homepage_url": "http://autopagerize.net/",
 +   "icons": {
 +      "128": "icons/icon128.png",
 +      "16": "icons/icon16.png",
 +      "32": "icons/icon32.png",
 +      "48": "icons/icon48.png"
 +   },
 +   "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCfCTQFR5CUHBXm+UoYq3ihpQVyUujLq9l68O/Epw7lVZNwM6y/IdeiRNI4WFrxAlHaTdoFWW61gCuWyOR2Iy+/b2HjpHao7ssA+l4X6OcHVKuQypWjxEomWDmaZnz9LWQmsqOvQ4RltPDQtU/ym6DGHooRPpzG9EaYnSl7fM879wIDAQAB",
 +   "manifest_version": 2,
 +   "name": "AutoPagerize",
 +   "options_page": "options.html",
 +   "page_action": {
 +      "default_icon": "icons/icon16.png",
 +      "default_popup": "popup.html",
 +      "default_title": "Autopagerize for Chrome"
 +   },
 +   "permissions": [ "http://wedata.net/*", "tabs" ],
 +   "update_url": "https://clients2.google.com/service/update2/crx",
 +   "version": "0.3.7",
 +   "web_accessible_resources": [ "error.html", "loading.html", "loading.gif" ]
++=======
+     }
+   ],
+   "description": "A browser Extension for auto loading paginated web pages. AutoPagerize use in many web site, and provide efficiently web browsing.",
+   "name": "AutoPagerize",
+   "permissions": [
+     "webRequest",
+     "webRequestBlocking",
+     "tabs",
+     "http://*/*",
+     "https://*/*"
+   ],
+   "background": {
+     "page": "background.html"
+   },
+   "options_page": "options.html",
+   "version": "0.3.5",
+   "icons": {
+      "16":  "icons/icon16.png",
+      "32":  "icons/icon32.png",
+      "48":  "icons/icon48.png",
+      "128": "icons/icon128.png"
+   },
+   "page_action": {
+     "default_title": "Autopagerize for Chrome",
+     "default_popup": "popup.html",
+     "default_icon": "icons/icon16.png"
+   },
+   "default_locale": "en",
+   "web_accessible_resources": [
+     "error.html",
+     "loading.html",
+     "loading.gif"
+   ]
++>>>>>>> use xhr with webRequest
  }

これを手動でマージしていきます.

# そのまま採用
git checkout --theirs src/background.js

manifest.json もマージします.結果,こんな形.

{
  "background": {
    "page": "background.html"
  },
  "content_scripts": [ {
    "js": [ "extension.js", "autopagerize.user.js" ],
    "matches": [ "http://*/*", "https://*/*" ],
    "run_at": "document_end"
  } ],
  "default_locale": "en",
  "description": "A browser Extension for auto loading paginated web pages. AutoPagerize use in many web site, and provide efficiently web browsing.",
  "homepage_url": "http://autopagerize.net/",
  "icons": {
    "16":  "icons/icon16.png",
    "32":  "icons/icon32.png",
    "48":  "icons/icon48.png",
    "128": "icons/icon128.png"
  },
  "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCfCTQFR5CUHBXm+UoYq3ihpQVyUujLq9l68O/Epw7lVZNwM6y/IdeiRNI4WFrxAlHaTdoFWW61gCuWyOR2Iy+/b2HjpHao7ssA+l4X6OcHVKuQypWjxEomWDmaZnz9LWQmsqOvQ4RltPDQtU/ym6DGHooRPpzG9EaYnSl7fM879wIDAQAB",
  "manifest_version": 2,
  "name": "AutoPagerize",
  "options_page": "options.html",
  "page_action": {
    "default_icon": "icons/icon16.png",
    "default_popup": "popup.html",
    "default_title": "Autopagerize for Chrome"
  },
  "permissions": [
    "webRequest",
    "webRequestBlocking",
    "tabs",
    "http://*/*",
    "https://*/*"
  ],
  "update_url": "https://clients2.google.com/service/update2/crx",
  "version": "0.3.7",
  "web_accessible_resources": [ "error.html", "loading.html", "loading.gif" ]
}

rebase を完了しておしまい.

git add .
git rebase --continue

2. chrome.webRequest パッチを適用 (2014/05/22 追記)

Chrome 35.0.1916.114 より,webRequest によるレスポンスヘッダーの修正時にコンフリクトするようになりました.

パッチを適用し,コンフリクトを解消します.

参考: chrome.webRequest

curl https://gist.githubusercontent.com/codeout/6835610480bbe5c6b981/raw/5a11706054c49d36a3a737f120815a217987b6a1/background.js | patch -p1

3. .crx をビルドする

必要なツールセットを用意.

gem install crxmake

次のようにbuild.rb を修正.

 require 'rubygems'
 require 'crxmake'

-CrxMake.zip(
+CrxMake.make(
   :ex_dir => "./src",
# private key は自動生成する
-  :pkey   => "./autopagerize_for_chrome.pem",
-  :zip_output => "./autopagerize_for_chrome.zip",
+  :crx_output => "./autopagerize_for_chrome.crx",
   :verbose => true,
   :ignorefile => /\.swp/,
   :ignoredir => /\.(?:svn|git|cvs)/

最後にビルド.

./build.rb

autopagerize_for_chrome.crx ができているはず.

4. Chrome に入れる

できたautopagerize_for_chrome.crx拡張機能ページにドラッグ&ドロップするだけです.

もとのAutoPagerize を無効化しておくのも忘れずに.

これでTumblr Dashboard でもAutoPagerize が動きます.幸せ.

注意

AutoPagerize を作った@swdyh さんもXHR には前向きっぽいのですが,まだ考え中のようです.XHR 版ブランチはきちんとテストされていない可能性もあります.

セキュリティに影響する変更なので, もう少し考えた上でマージするか決めようと思っています.

Work with X-Frame-Options:deny on Chrome #3

コードを眺めると別の仕組み(webRequest) でURL のスキーム/ホストが同じことを確かめているようですし,まあ大丈夫かなあと.しばらく使ってみようと思います.

ポートの確認はしてませんが...まあいいでしょうかね.