編集画面が多重に出る障害について

2018/01/08

概要

SCP財団に下記の障害報告がありました。

自分がフォーラムスレッドに投稿したコメントの修正を行おうとして「編集」をタップすると、編集するためのテキストボックスが2つ同時に出現してしまいます。しかも下側はダミーらしく、下のボックスで編集を終えて保存すると、変更が反映されずに「何も変更されていないリビジョン」だけが残ってしまいます。上側のボックスから編集すれば正常に保存されます。
環境はiPadのSafariです。

これはwikidot自体の不具合なんでしょうか?他にも再現した方いらっしゃいましたら教えてください。

(追記)同時に3個のテキストボックスが出現した例も確認されました。どうもテキストボックスが表示される前に「編集」を複数回連続でタップすると再現しやすいようです。

これは実際に起こる事です。

syouko.PNG

これの原因を特定します。

結論

複数の編集画面が出る事について: Wikidotのサーバーとの通信でタイムラグが大きい場合、編集画面を多重で出す事を防ぐ処理が正常に行われず、複数の編集画面が表示される。

下の方がダミーである理由: 同一のIDの要素が複数出現する為、javascriptの仕様(というよりIDというパラメータの仕様)上一番上にある編集画面の要素しか取得できない為、下の編集画面で編集をおこなっても編集が反映されない。

注意点: これは「編集」だけでなく「返信」や「新しくコメントを投稿する」と言った、編集エリアを表示させる全ての動作において発生し得る事です。

対策

編集ボタンを押したら編集画面が出るまで一息待つのを推奨します。

原因

重要な事は編集エリアとは既にフォーラムに存在するものではなく、Wikidotの処理によってページに後から追加されるという事です。この事を念頭に踏まえて下記の文章をお読みください。

編集をクリックした際、下記のメソッドが動作します。これは「編集」ボタンのonclickイベントと紐づいている為、編集ボタンを押した直後に発動します。
WIKIDOT.modules.ForumViewThreadModule.listeners:

  editPost: function (f, b) {
  //↓多分Wikidotサイトからの命令かを判定している
    if (WIKIDOT.Editor.editElementId) {
    //既に編集エリアが存在し、編集エリアに文字がある場合
      if ($("np-text") && $("np-text").value != "") {
    //警告文を表示し、処理を終了する
        var a = new OZONE.dialogs.ErrorDialog();
        a.content = 'You have an active editor somewhere already and it is not possible to edit multiple elements at once.<br/><br/>(<a href="javascript:;" onclick="OZONE.visuals.scrollTo(\'' + WIKIDOT.Editor.editElementId + "');OZONE.dialog.cleanAll()\">scroll to active editor</a>)";
        a.show();
        return
      } else {
    //編集エリアが開かれてない、もしくは編集エリアが空白の場合
        var c = $("new-post-form-container");
        c.parentNode.removeChild(c);
    //既に存在する編集エリアを削除する(重要)
        $("new-post-button").style.display = "";
        WIKIDOT.Editor.shutDown()
      }
    }
    //サーバーから対象となるポストのデータを取得する
    var d = new Object();
    d.postId = b;
    d.threadId = WIKIDOT.forumThreadId;
    OZONE.ajax.requestModule("forum/sub/ForumEditPostFormModule", d, WIKIDOT.modules.ForumViewThreadModule.callbacks.editPost)
  },

ここでは大雑把に言えば編集エリアが複数出現する事を防止する処理が行われています。

このメソッドで重要なのは、下記の2点です。

  1. 既存の編集エリアの削除は「編集」ボタンを押した直後に行われる
  2. この処理によって消される編集エリアは1度につき1つ

さて、既に述べた様に編集エリアとはプログラムの処理によって後からページに追加されるものです。追加される動作は下記のメソッドで行われています。
WIKIDOT.modules.ForumViewThreadModule.callbacks:

  editPost: function (c) {
    if (!WIKIDOT.utils.handleError(c)) {
      return
    }
    //編集エリアを生成しサーバーから取得した情報を格納する
    var a = document.createElement("div");
    a.id = "edit-post-form-container";
    $j(a).html(c.body);
    var d = $("fpc-" + c.postId);
    var b = $("post-" + c.postId);
    OZONE.dom.insertAfter(d, a, b);
    WIKIDOT.Editor.init("np-text", "np-editor-panel");
    setTimeout('OZONE.visuals.scrollTo("edit-post-form-container")', 300)
  },

ここで新たにDivという要素を作り、その中に編集エリアを生成した後、その編集エリアまで画面をスクロールさせるという処理をおこなっています。そしてこの新たに生成された要素にはサーバーから取得したデータが格納されます。

重要なのは、この要素の追加がサーバーとの通信の後に行われるという事です。

上記の要素を追加するメソッドは(この動作から考えるに)サーバーからのデータ取得が行われた後に動作する様に設定されています。

WIKIDOT.modules.ForumViewThreadModule.callbacks.editPost)

従って、サーバーとの通信が終了しなければ要素の追加が行われない為、ここには「編集」ボタンを押してからのタイムラグが発生します。そして、更に編集エリアが既に存在するかという判定及び既に存在した場合に編集エリアを消すという動作は「編集」ボタンを押した直後に行われます。

つまり、個数を制御する動作と編集エリアを追加する動作は異なるタイミングで行われているという事です。

その為、サーバーとの通信によるタイムラグが大きい場合、簡略的に表すと以下の動作が行われる事になります(実際はもう少し複雑な動作ですが、これを突っ込める人は自分でわかると思うので割愛します)。

1.「編集」ボタンを連打する
2. 連打した分の通信が発生
3.(タイムラグのせいでまだ上記の通信が終わらず、要素の追加が行われていないにも関わらず)押した回数分、編集エリアの存在有無判定等が行われる
4. 「編集」ボタンを押した回数分の通信終了
5. 通信が終了した回数分、ページに編集エリアが追加される

要するに編集エリアの個数を制御する処理が全て終わった後に編集エリアを追加する処理が行われるのです。この場合、編集エリアの追加は全く制御を受けずに行われます。これが編集エリアが多重で出現する原因です。

また、ここで複数生成された要素は皆同一のIDを持ちます。IDによる要素判定では一番上にある要素しか取得できない為、下に配置された要素(編集エリア)では文字を取得できず無意味なダミーと言える動作が行われるようになります。

なお、編集エリアを出す動作はメソッド名が違うだけで大筋ではここで提示した「サーバーとの通信→要素の追加」という動作が行われる為、この現象は編集エリアを出す全ての動作において発生し得ます。

対策は「編集」ボタンを押したら一息つくという事が挙げられます。

Wikidotはどうするべきだったか

コールバック関数の中にも編集エリアの存在を判定する処理を入れた方が良かったです。
Wikidotにバグレポート提出済です。

特に指定がない限り、このサイトのコンテンツには次のライセンスが適用されます: Creative Commons Attribution-ShareAlike 3.0 License