/* Any copyright is dedicated to the public domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */

// Bug 905573 - Add setInputMethodActive to browser elements to allow gaia
// system set the active IME app.
'use strict';

SimpleTest.waitForExplicitFinish();
browserElementTestHelpers.setEnabledPref(true);
browserElementTestHelpers.addPermission();

function setup() {
  let appInfo = SpecialPowers.Cc['@mozilla.org/xre/app-info;1']
                .getService(SpecialPowers.Ci.nsIXULAppInfo);
  if (appInfo.name != 'B2G') {
    SpecialPowers.Cu.import("resource://gre/modules/Keyboard.jsm", window);
  }

  SpecialPowers.setBoolPref("dom.mozInputMethod.enabled", true);
  SpecialPowers.setBoolPref("dom.mozInputMethod.testing", true);
  SpecialPowers.addPermission('input-manage', true, document);
}

function tearDown() {
  SpecialPowers.setBoolPref("dom.mozInputMethod.enabled", false);
  SpecialPowers.setBoolPref("dom.mozInputMethod.testing", false);
  SpecialPowers.removePermission('input-manage', document);
  SimpleTest.finish();
}

function runTest() {
  let path = location.pathname;
  let imeUrl = location.protocol + '//' + location.host +
               path.substring(0, path.lastIndexOf('/')) +
               '/file_inputmethod.html';
  SpecialPowers.pushPermissions([{
    type: 'input',
    allow: true,
    context: {
      url: imeUrl,
      appId: SpecialPowers.Ci.nsIScriptSecurityManager.NO_APP_ID,
      isInBrowserElement: true
    }
  }], createFrames);
}

var gFrames = [];
var gInputFrame;

function createFrames() {
  // Create two input method iframes.
  let loadendCount = 0;
  let countLoadend = function() {
    ok(this.setInputMethodActive, 'Can access setInputMethodActive.');

    if (this === gInputFrame) {
      // The frame script running in the frame where the input is hosted.
      let appFrameScript = function appFrameScript() {
        let input = content.document.body.firstElementChild;
        input.oninput = function() {
          sendAsyncMessage('test:InputMethod:oninput', this.value);
        };

        /*
         * Bug 957213. Sometimes we need to refocus the input field to avoid
         * intermittent test failure.
         */
        content.setInterval(function() {
          input.focus();
        }, 500);
      }

      // Inject frame script to receive input.
      let mm = SpecialPowers.getBrowserFrameMessageManager(gInputFrame);
      mm.loadFrameScript('data:,(' + appFrameScript.toString() + ')();', false);
      mm.addMessageListener("test:InputMethod:oninput", next);
    }

    loadendCount++;
    if (loadendCount === 3) {
      startTest();
    }
  };

  // Create an input field to receive string from input method iframes.
  gInputFrame = document.createElement('iframe');
  SpecialPowers.wrap(gInputFrame).mozbrowser = true;
  gInputFrame.src =
    'data:text/html,<input autofocus value="hello" />' +
    '<p>This is targetted mozbrowser frame.</p>';
  document.body.appendChild(gInputFrame);
  gInputFrame.addEventListener('mozbrowserloadend', countLoadend);

  for (let i = 0; i < 2; i++) {
    let frame = gFrames[i] = document.createElement('iframe');
    SpecialPowers.wrap(gFrames[i]).mozbrowser = true;
    // When the input method iframe is activated, it will send the URL
    // hash to current focused element. We set different hash to each
    // iframe so that iframes can be differentiated by their hash.
    frame.src = 'file_inputmethod.html#' + i;
    document.body.appendChild(frame);
    frame.addEventListener('mozbrowserloadend', countLoadend);
  }
}

function startTest() {
  // Set focus to the input field and wait for input methods' inputting.
  SpecialPowers.DOMWindowUtils.focus(gInputFrame);

  let req0 = gFrames[0].setInputMethodActive(true);
  req0.onsuccess = function() {
    ok(true, 'setInputMethodActive succeeded (0).');
  };

  req0.onerror = function() {
    ok(false, 'setInputMethodActive failed (0): ' + this.error.name);
  };
}

var gTimerId = null;
var gCount = 0;

function next(msg) {
  gCount++;
  let wrappedMsg = SpecialPowers.wrap(msg);
  let value = wrappedMsg.data;
  // The texts sent from the first and the second input method are '#0' and
  // '#1' respectively.
  switch (gCount) {
    case 1:
      is(value, '#0hello',
         'Failed to get correct input from the first iframe.');
      let req1 = gFrames[1].setInputMethodActive(true);
      req1.onsuccess = function() {
       ok(true, 'setInputMethodActive succeeded (1).');
      };
      req1.onerror = function() {
       ok(false, 'setInputMethodActive failed (1): ' + this.error.name);
      };
      break;

    case 2:
      is(value, '#0#1hello',
         'Failed to get correct input from the second iframe.');
      // Do nothing and wait for the next input from the second iframe.
      break;

    case 3:
      is(value, '#0#1#1hello',
         'Failed to get correct input from the second iframe.');
      // Receive the second input from the second iframe.
      // Deactive the second iframe.
      let req3 = gFrames[1].setInputMethodActive(false);
      req3.onsuccess = function() {
        ok(true, 'setInputMethodActive(false) succeeded (3).');
      };
      req3.onerror = function() {
        ok(false, 'setInputMethodActive(false) failed (3): ' + this.error.name);
      };

      // Wait for a short while to ensure the second iframe is not active any
      // more.
      gTimerId = setTimeout(function() {
        ok(true, 'Successfully deactivate the second iframe.');
        tearDown();
      }, 1000);
      break;

    case 4:
      ok(false, 'Failed to deactivate the second iframe in time.');
      clearTimeout(gTimerId);
      tearDown();
      break;
  }
}

setup();
addEventListener('testready', runTest);
