IT戦記 このページをアンテナに追加 RSSフィード Twitter

2008-10-19

サーバー起動用に JavaScript が 1 秒以上実行されなくて alert しないブラウザを作る

f:id:amachang:20081019211736p:image

はじめに

サムネイルサーバーのようなサービスを作るときには、ウェブサーバー上にブラウザを乗せる必要があります。

問題

ただ、そういった用途でブラウザを使う場合。

JavaScript無限ループに落ち入らないように配慮する必要があります。

ほとんどの場合は、 JavaScript 自体をオフにすることが多いのですが JavaScript を実行したいような場合もあるでしょう。

解決方法

今回は、 WebKit でその解決方法を紹介します。

1. まず WebKitソースコードを取得する
svn co http://svn.webkit.org/repository/webkit/trunk WebKit
2. 次に、タイマーの時間を短くする

WebCore/bindings/js/JSDOMWindowBase.cpp を編集

JSDOMWindowBase::JSDOMWindowBase(PassRefPtr<StructureID> structure, PassRefPtr<DOMWindow> window, JSDOMWindowShell* shell)
    : JSGlobalObject(structure, new JSDOMWindowBaseData(window, shell), shell)
{
    // Time in milliseconds before the script timeout handler kicks in.
    // ■ ここを 10000 から 1000 にする
    setTimeoutTime(1000);

    GlobalPropertyInfo staticGlobals[] = {
        GlobalPropertyInfo(Identifier(globalExec(), "document"), jsNull(), DontDelete | ReadOnly),
        GlobalPropertyInfo(Identifier(globalExec(), "window"), d()->shell, DontDelete | ReadOnly)
    };   
    
    addStaticGlobals(staticGlobals, sizeof(staticGlobals) / sizeof(GlobalPropertyInfo));
}
3. 次に、 JavaScript が 1 秒以上実行されると、無条件(確認なし)で終了

WebCore/page/Chrome.cpp を編集

bool Chrome::shouldInterruptJavaScript()
{
    // ■ この行を追加
    return true;

    // Defer loads in case the client method runs a new event loop that would 
    // otherwise cause the load to continue while we're in the middle of executing JavaScript.
    PageGroupLoadDeferrer deferrer(m_page, true);

    return m_client->shouldInterruptJavaScript();
}
4. alert, confirm, prompt を切る

WebCore/page/DOMWindow.cpp を編集

void DOMWindow::alert(const String& message)
{
    // ■ この行を追加   
    return;

    if (!m_frame)
        return;
    
    Document* doc = m_frame->document();
    ASSERT(doc);
    if (doc)
        doc->updateRendering();
    
    Page* page = m_frame->page();
    if (!page)
        return;

    page->chrome()->runJavaScriptAlert(m_frame, message);
}   

bool DOMWindow::confirm(const String& message)
{
    // ■ この行を追加   
    return false;

    if (!m_frame)
        return false;

    Document* doc = m_frame->document();
    ASSERT(doc);
    if (doc)
        doc->updateRendering();

    Page* page = m_frame->page();
    if (!page)
        return false;

    return page->chrome()->runJavaScriptConfirm(m_frame, message);
}

String DOMWindow::prompt(const String& message, const String& defaultValue)
{
    // ■ この行を追加   
    return String();

    if (!m_frame)
        return String();

    Document* doc = m_frame->document();
    ASSERT(doc);
    if (doc)
        doc->updateRendering();

    Page* page = m_frame->page();
    if (!page)
        return String();

    String returnValue;
    if (page->chrome()->runJavaScriptPrompt(m_frame, message, defaultValue, returnValue))
        return returnValue;

    return String();
}
5. window.open, window.showModalDialog によるポップアップを切る

WebCore/bindings/js/JSDOMWindowBase.cpp を編集

static bool allowPopUp(ExecState* exec)
{
    // ■ この行を追加
    return false;

    Frame* frame = asJSDOMWindow(exec->dynamicGlobalObject())->impl()->frame();

    ASSERT(frame);
    if (frame->script()->processingUserGesture())
        return true;
    Settings* settings = frame->settings();
    return settings && settings->JavaScriptCanOpenWindowsAutomatically();
}
補足:ここまでの diff です
Index: WebCore/bindings/js/JSDOMWindowBase.cpp
===================================================================
--- WebCore/bindings/js/JSDOMWindowBase.cpp	(revision 37703)
+++ WebCore/bindings/js/JSDOMWindowBase.cpp	(working copy)
@@ -175,7 +175,7 @@
     : JSGlobalObject(structure, new JSDOMWindowBaseData(window, shell), shell)
 {
     // Time in milliseconds before the script timeout handler kicks in.
-    setTimeoutTime(10000);
+    setTimeoutTime(1000);
 
     GlobalPropertyInfo staticGlobals[] = {
         GlobalPropertyInfo(Identifier(globalExec(), "document"), jsNull(), DontDelete | ReadOnly),
@@ -223,6 +223,8 @@
 
 static bool allowPopUp(ExecState* exec)
 {
+    return false;
+
     Frame* frame = asJSDOMWindow(exec->dynamicGlobalObject())->impl()->frame();
 
     ASSERT(frame);
Index: WebCore/page/DOMWindow.cpp
===================================================================
--- WebCore/page/DOMWindow.cpp	(revision 37703)
+++ WebCore/page/DOMWindow.cpp	(working copy)
@@ -469,6 +469,8 @@
 
 void DOMWindow::alert(const String& message)
 {
+    return;
+
     if (!m_frame)
         return;
 
@@ -486,6 +488,8 @@
 
 bool DOMWindow::confirm(const String& message)
 {
+    return false;
+
     if (!m_frame)
         return false;
 
@@ -503,6 +507,8 @@
 
 String DOMWindow::prompt(const String& message, const String& defaultValue)
 {
+    return String();
+
     if (!m_frame)
         return String();
 
Index: WebCore/page/Chrome.cpp
===================================================================
--- WebCore/page/Chrome.cpp	(revision 37703)
+++ WebCore/page/Chrome.cpp	(working copy)
@@ -302,6 +302,8 @@
 
 bool Chrome::shouldInterruptJavaScript()
 {
+    return true;
+
     // Defer loads in case the client method runs a new event loop that would 
     // otherwise cause the load to continue while we're in the middle of executing JavaScript.
     PageGroupLoadDeferrer deferrer(m_page, true);
6. ビルドします

今回は Gtk を使う例です。

$ cd WebKit
$ WebKitTools/Scripts/update-webkit
$ WebKitTools/Scripts/set-webkit-configuration --release
$ WebKitTools/Scripts/build-webkit --gtk

Configure の途中で「bison がないよ><」とか「flex がないよ><」とかいろいろ言われると思いますので、その都度インストールしてくださいね。

7. 起動

さあ、完成です。簡単ですね!

さっそく起動しましょう。

$ WebKitBuild/Release/Programs/GtkLaucher

起動しました!

このブラウザで様々なページを見てみてください!

どんなに重いページでも JavaScript が 1 秒以上連続で実行されませんね。

さらに様々なポップアップや alert 系のウィンドウも出ません。

ばっちりですね!

その他のこと

ただ、これだけで安心という訳ではありません。

いちおう Cookie や Storage 関係も切っておいたほうがいいでしょう。

Storage は build-webkit で --no-storage とか指定すればできます。

まとめ

今日は初めてブラウザのコードに手を入れた記念日。

Ubuntu Server インストールめも

準備

ダウンロード

http://www.ubuntu.com/getubuntu/download

  • Ubuntu 8.04 LTS Server Edition
  • 64bit AMD and Intel computers (64 bit マシンなので)

で、ダウンロード

CD を焼く

ディスクユーティリティとかで

インストール

  • CD を入れてブート
  • Language
    • 「日本語」を選択
  • Ubuntu ロゴが表示される
    • 「Install Ubuntu Server」
    • ちょっと待つ(数十秒)
  • Ubuntu インストーラメニュー
    • 「Alt+Shift」を選択
    • ちょっと待つ(数十秒)
  • ホスト名
  • ディスクのパーティショニング
    • 「ガイド - ディスク全体を使う」を選択
    • 「はい」を選択
    • けっこう待つ(数分)
  • ユーザーとパスワードのセットアップ
  • HTTP プロキシの設定
    • なし
  • Software selection
    • とりあえず「LAMP server」「OpenSSH server」を選択
    • mysql-server-5.0 を設定しています。
    • ちょっと待つ(数分)
  • インストールの完了

はやっ

セットアップ

$ sudo apt-get update
  • .vimrc を書く
syntax on

set expandtab
set tabstop=4
set shiftwidth=4
set softtabstop=0
set number
set smartindent
set incsearch

怒られた

Error detected while processing /home/amachang/.vimrc:
line    2:
E319: Sorry, the command is not available in this version: syntax on
Press ENTER or type command to continue

あたらしく vimインストールする

$ sudo apt-get install vim

おk

まとめ

サーバーで何をするかを考えていなかった