mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1157637 - Create ActionBar Handler and Gecko SelectionCarets tests, r=margaret
This commit is contained in:
parent
e6219c5f52
commit
0c6120edf3
@ -23,6 +23,7 @@ import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
|
|||||||
import org.mozilla.gecko.favicons.RemoteFavicon;
|
import org.mozilla.gecko.favicons.RemoteFavicon;
|
||||||
import org.mozilla.gecko.gfx.BitmapUtils;
|
import org.mozilla.gecko.gfx.BitmapUtils;
|
||||||
import org.mozilla.gecko.gfx.Layer;
|
import org.mozilla.gecko.gfx.Layer;
|
||||||
|
import org.mozilla.gecko.mozglue.RobocopTarget;
|
||||||
import org.mozilla.gecko.toolbar.BrowserToolbar.TabEditingState;
|
import org.mozilla.gecko.toolbar.BrowserToolbar.TabEditingState;
|
||||||
import org.mozilla.gecko.util.ThreadUtils;
|
import org.mozilla.gecko.util.ThreadUtils;
|
||||||
|
|
||||||
@ -146,6 +147,7 @@ public class Tab {
|
|||||||
Tabs.getInstance().notifyListeners(this, Tabs.TabEvents.CLOSED);
|
Tabs.getInstance().notifyListeners(this, Tabs.TabEvents.CLOSED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RobocopTarget
|
||||||
public int getId() {
|
public int getId() {
|
||||||
return mId;
|
return mId;
|
||||||
}
|
}
|
||||||
|
@ -168,6 +168,7 @@ skip-if = android_version == "10" || android_version == "18"
|
|||||||
skip-if = android_version == "18"
|
skip-if = android_version == "18"
|
||||||
[testStateWhileLoading]
|
[testStateWhileLoading]
|
||||||
|
|
||||||
|
[testSelectionCarets]
|
||||||
# testSelectionHandler disabled on Android 2.3 by trailing skip-if, due to bug 980074
|
# testSelectionHandler disabled on Android 2.3 by trailing skip-if, due to bug 980074
|
||||||
# also disabled on Android 4.3, bug 1144882
|
# also disabled on Android 4.3, bug 1144882
|
||||||
[testSelectionHandler]
|
[testSelectionHandler]
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>ActionBar Handler and SelectionCarets tests</title>
|
||||||
|
<meta name="viewport"
|
||||||
|
content="initial-scale=1, allowZoom=no, maximum-scale=1,
|
||||||
|
user-scalable=no, width=device-width">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="LTRcontenteditable"
|
||||||
|
style="direction: ltr; width: 10em; height: 2em; word-wrap: break-word;
|
||||||
|
overflow: auto; -moz-user-select:text"
|
||||||
|
contenteditable="true">Find my book</div>
|
||||||
|
<div id="RTLcontenteditable"
|
||||||
|
style="direction: rtl; width: 10em; height: 2em; word-wrap: break-word;
|
||||||
|
overflow: auto; -moz-user-select:text"
|
||||||
|
contenteditable="true">איפה האוטו שלי</div>
|
||||||
|
|
||||||
|
<div id="LTRtextContent"
|
||||||
|
style="direction: ltr; width: 10em; height: 2em; word-wrap: break-word;
|
||||||
|
overflow: auto; -moz-user-select:text">Open the door</div>
|
||||||
|
<div id="RTLtextContent"
|
||||||
|
style="direction: rtl; width: 10em; height: 2em; word-wrap: break-word;
|
||||||
|
overflow: auto; -moz-user-select:text">תן לי מים</div>
|
||||||
|
|
||||||
|
<input id="LTRinput" style="direction: ltr;" value="Type something">
|
||||||
|
<input id="RTLinput" style="direction: rtl;" value="לרוץ במעלה הגבעה">
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<textarea id="LTRtextarea" style="direction: ltr;"
|
||||||
|
rows="3" cols="8">Words in a box</textarea>
|
||||||
|
<textarea id="RTLtextarea" style="direction: rtl;"
|
||||||
|
rows="3" cols="8">הספר הוא טוב</textarea>
|
||||||
|
</body>
|
||||||
|
</html>
|
106
mobile/android/tests/browser/robocop/testSelectionCarets.java
Normal file
106
mobile/android/tests/browser/robocop/testSelectionCarets.java
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mozilla.gecko.tests;
|
||||||
|
|
||||||
|
import org.mozilla.gecko.AppConstants;
|
||||||
|
import org.mozilla.gecko.EventDispatcher;
|
||||||
|
import org.mozilla.gecko.GeckoAppShell;
|
||||||
|
import org.mozilla.gecko.GeckoEvent;
|
||||||
|
import org.mozilla.gecko.Tab;
|
||||||
|
import org.mozilla.gecko.Tabs;
|
||||||
|
import org.mozilla.gecko.util.GeckoEventListener;
|
||||||
|
|
||||||
|
import android.os.SystemClock;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
public class testSelectionCarets extends JavascriptTest implements GeckoEventListener {
|
||||||
|
private static final String LOGTAG = "testSelectionCarets";
|
||||||
|
|
||||||
|
private static final String LONGPRESS_EVENT = "testSelectionCarets:Longpress";
|
||||||
|
private static final String TAB_CHANGE_EVENT = "testSelectionCarets:TabChange";
|
||||||
|
|
||||||
|
private final TabsListener tabsListener;
|
||||||
|
|
||||||
|
public testSelectionCarets() {
|
||||||
|
super("testSelectionCarets.js");
|
||||||
|
|
||||||
|
tabsListener = new TabsListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
|
||||||
|
Tabs.registerOnTabsChangedListener(tabsListener);
|
||||||
|
EventDispatcher.getInstance().registerGeckoThreadListener(this, LONGPRESS_EVENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void testJavascript() throws Exception {
|
||||||
|
// This feature is currently only available in Nightly.
|
||||||
|
if (!AppConstants.NIGHTLY_BUILD) {
|
||||||
|
mAsserter.dumpLog(LOGTAG + " is disabled on non-Nightly builds: returning");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
super.testJavascript();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
Tabs.unregisterOnTabsChangedListener(tabsListener);
|
||||||
|
EventDispatcher.getInstance().unregisterGeckoThreadListener(this, LONGPRESS_EVENT);
|
||||||
|
|
||||||
|
super.tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The test script will request us to trigger Longpress AndroidGeckoEvents.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void handleMessage(String event, final JSONObject message) {
|
||||||
|
switch(event) {
|
||||||
|
case LONGPRESS_EVENT: {
|
||||||
|
final long meTime = SystemClock.uptimeMillis();
|
||||||
|
final int meX = Math.round(message.optInt("x", 0));
|
||||||
|
final int meY = Math.round(message.optInt("y", 0));
|
||||||
|
final MotionEvent motionEvent =
|
||||||
|
MotionEvent.obtain(meTime, meTime, MotionEvent.ACTION_DOWN, meX, meY, 0);
|
||||||
|
|
||||||
|
final GeckoEvent geckoEvent = GeckoEvent.createLongPressEvent(motionEvent);
|
||||||
|
GeckoAppShell.sendEventToGecko(geckoEvent);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observes tab change events to broadcast to the test script.
|
||||||
|
*/
|
||||||
|
private class TabsListener implements Tabs.OnTabsChangedListener {
|
||||||
|
@Override
|
||||||
|
public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) {
|
||||||
|
switch (msg) {
|
||||||
|
case STOP:
|
||||||
|
final JSONObject args = new JSONObject();
|
||||||
|
try {
|
||||||
|
args.put("tabId", tab.getId());
|
||||||
|
args.put("event", msg.toString());
|
||||||
|
} catch (JSONException e) {
|
||||||
|
Log.e(LOGTAG, "Error building JSON arguments for " + TAB_CHANGE_EVENT, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final GeckoEvent event =
|
||||||
|
GeckoEvent.createBroadcastEvent(TAB_CHANGE_EVENT, args.toString());
|
||||||
|
GeckoAppShell.sendEventToGecko(event);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
244
mobile/android/tests/browser/robocop/testSelectionCarets.js
Normal file
244
mobile/android/tests/browser/robocop/testSelectionCarets.js
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||||
|
Cu.import("resource://gre/modules/Services.jsm");
|
||||||
|
Cu.import("resource://gre/modules/Messaging.jsm");
|
||||||
|
Cu.import('resource://gre/modules/Geometry.jsm');
|
||||||
|
|
||||||
|
const SELECTION_CARETS_PREF = "selectioncaret.enabled";
|
||||||
|
const TOUCH_CARET_PREF = "touchcaret.enabled";
|
||||||
|
const TEST_URL = "http://mochi.test:8888/tests/robocop/testSelectionCarets.html";
|
||||||
|
|
||||||
|
// After longpress, Gecko notifys ActionBarHandler to init then update state.
|
||||||
|
// When it does, we'll peek over its shoulder and test status.
|
||||||
|
const LONGPRESS_EVENT = "testSelectionCarets:Longpress";
|
||||||
|
const STATUS_UPDATE_EVENT = "ActionBar:UpdateState";
|
||||||
|
|
||||||
|
// Ensures Tabs are completely loaded, viewport and zoom constraints updated, etc.
|
||||||
|
const TAB_CHANGE_EVENT = "testSelectionCarets:TabChange";
|
||||||
|
const TAB_STOP_EVENT = "STOP";
|
||||||
|
|
||||||
|
const gChromeWin = Services.wm.getMostRecentWindow("navigator:browser");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Robocop test helpers.
|
||||||
|
*/
|
||||||
|
function ok(passed, text) {
|
||||||
|
do_report_result(passed, text, Components.stack.caller, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function is(lhs, rhs, text) {
|
||||||
|
do_report_result(lhs === rhs, "[ " + lhs + " === " + rhs + " ] " + text,
|
||||||
|
Components.stack.caller, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for and return, when an expected tab change event occurs.
|
||||||
|
*
|
||||||
|
* @param tabId, The id of the target tab we're observing.
|
||||||
|
* @param eventType, The event type we expect.
|
||||||
|
* @return {Promise}
|
||||||
|
* @resolves The tab change object, including the matched tab id and event.
|
||||||
|
*/
|
||||||
|
function do_promiseTabChangeEvent(tabId, eventType) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
let observer = (subject, topic, data) => {
|
||||||
|
let message = JSON.parse(data);
|
||||||
|
|
||||||
|
if (message.event === eventType && message.tabId === tabId) {
|
||||||
|
Services.obs.removeObserver(observer, TAB_CHANGE_EVENT);
|
||||||
|
resolve(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Services.obs.addObserver(observer, TAB_CHANGE_EVENT, false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selection methods vary if we have an input / textarea element,
|
||||||
|
* or if we have basic content.
|
||||||
|
*/
|
||||||
|
function isInputOrTextarea(element) {
|
||||||
|
return ((element instanceof Ci.nsIDOMHTMLInputElement) ||
|
||||||
|
(element instanceof Ci.nsIDOMHTMLTextAreaElement));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the selection controller based on element.
|
||||||
|
*/
|
||||||
|
function elementSelection(element) {
|
||||||
|
return (isInputOrTextarea(element)) ?
|
||||||
|
element.editor.selection :
|
||||||
|
element.ownerDocument.defaultView.getSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select the first character of a target element, w/o affecting focus.
|
||||||
|
*/
|
||||||
|
function selectElementFirstChar(doc, element) {
|
||||||
|
if (isInputOrTextarea(element)) {
|
||||||
|
element.setSelectionRange(0, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple test cases designed firstChild == #text node.
|
||||||
|
let range = doc.createRange();
|
||||||
|
range.setStart(element.firstChild, 0);
|
||||||
|
range.setEnd(element.firstChild, 1);
|
||||||
|
|
||||||
|
let selection = elementSelection(element);
|
||||||
|
selection.removeAllRanges();
|
||||||
|
selection.addRange(range);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get longpress point. Determine the midpoint in the first character of
|
||||||
|
* the content in the element. X will be midpoint from left to right.
|
||||||
|
* Y will be 1/3 of the height up from the bottom to account for both
|
||||||
|
* LTR and smaller RTL characters. ie: |X| vs. |א|
|
||||||
|
*/
|
||||||
|
function getFirstCharPressPoint(doc, element, expected) {
|
||||||
|
// Select the first char in the element.
|
||||||
|
selectElementFirstChar(doc, element);
|
||||||
|
|
||||||
|
// Reality check selected char to expected.
|
||||||
|
let selection = elementSelection(element);
|
||||||
|
is(selection.toString(), expected, "Selected char should match expected char.");
|
||||||
|
|
||||||
|
// Return a point where long press should select entire word.
|
||||||
|
let rect = selection.getRangeAt(0).getBoundingClientRect();
|
||||||
|
let r = new Point(rect.left + (rect.width / 2), rect.bottom - (rect.height / 3));
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Long press an element (RTL/LTR) at its calculated first character
|
||||||
|
* position, and return the result.
|
||||||
|
*
|
||||||
|
* @param midPoint, The screen coord for the longpress.
|
||||||
|
* @return {Promise}
|
||||||
|
* @resolves The ActionBar status, including its target focused element, and
|
||||||
|
* the selected text that it sees.
|
||||||
|
*/
|
||||||
|
function do_promiseLongPressResult(midPoint) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
let observer = (subject, topic, data) => {
|
||||||
|
let ActionBarHandler = gChromeWin.ActionBarHandler;
|
||||||
|
if (topic === STATUS_UPDATE_EVENT) {
|
||||||
|
let text = ActionBarHandler._getSelectedText();
|
||||||
|
if (text !== "") {
|
||||||
|
// Remove notification observer, and resolve.
|
||||||
|
Services.obs.removeObserver(observer, STATUS_UPDATE_EVENT);
|
||||||
|
resolve({
|
||||||
|
focusedElement: ActionBarHandler._targetElement,
|
||||||
|
text: text,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add notification observer, trigger the longpress and wait.
|
||||||
|
Services.obs.addObserver(observer, STATUS_UPDATE_EVENT, false);
|
||||||
|
Messaging.sendRequestForResult({
|
||||||
|
type: LONGPRESS_EVENT,
|
||||||
|
x: midPoint.x,
|
||||||
|
y: midPoint.y,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main test method.
|
||||||
|
*/
|
||||||
|
add_task(function* testSelectionCarets() {
|
||||||
|
// Wait to start loading our test page until after the initial browser tab is
|
||||||
|
// completely loaded. This allows each tab to complete its layer initialization,
|
||||||
|
// importantly, its viewport and zoomContraints info.
|
||||||
|
let BrowserApp = gChromeWin.BrowserApp;
|
||||||
|
yield do_promiseTabChangeEvent(BrowserApp.selectedTab.id, TAB_STOP_EVENT);
|
||||||
|
|
||||||
|
// Ensure Gecko Selection and Touch carets are enabled.
|
||||||
|
Services.prefs.setBoolPref(SELECTION_CARETS_PREF, true);
|
||||||
|
Services.prefs.setBoolPref(TOUCH_CARET_PREF, true);
|
||||||
|
|
||||||
|
// Load test page, wait for load completion, register cleanup.
|
||||||
|
let browser = BrowserApp.addTab(TEST_URL).browser;
|
||||||
|
let tab = BrowserApp.getTabForBrowser(browser);
|
||||||
|
yield do_promiseTabChangeEvent(tab.id, TAB_STOP_EVENT);
|
||||||
|
|
||||||
|
do_register_cleanup(function cleanup() {
|
||||||
|
Services.prefs.clearUserPref(SELECTION_CARETS_PREF);
|
||||||
|
Services.prefs.clearUserPref(TOUCH_CARET_PREF);
|
||||||
|
BrowserApp.closeTab(tab);
|
||||||
|
});
|
||||||
|
|
||||||
|
// References to test document elements.
|
||||||
|
let doc = browser.contentDocument;
|
||||||
|
let ce_LTR_elem = doc.getElementById("LTRcontenteditable");
|
||||||
|
let tc_LTR_elem = doc.getElementById("LTRtextContent");
|
||||||
|
let i_LTR_elem = doc.getElementById("LTRinput");
|
||||||
|
let ta_LTR_elem = doc.getElementById("LTRtextarea");
|
||||||
|
|
||||||
|
let ce_RTL_elem = doc.getElementById("RTLcontenteditable");
|
||||||
|
let tc_RTL_elem = doc.getElementById("RTLtextContent");
|
||||||
|
let i_RTL_elem = doc.getElementById("RTLinput");
|
||||||
|
let ta_RTL_elem = doc.getElementById("RTLtextarea");
|
||||||
|
|
||||||
|
// Locate longpress midpoints for test elements, ensure expactations.
|
||||||
|
let ce_LTR_midPoint = getFirstCharPressPoint(doc, ce_LTR_elem, "F");
|
||||||
|
let tc_LTR_midPoint = getFirstCharPressPoint(doc, tc_LTR_elem, "O");
|
||||||
|
let i_LTR_midPoint = getFirstCharPressPoint(doc, i_LTR_elem, "T");
|
||||||
|
let ta_LTR_midPoint = getFirstCharPressPoint(doc, ta_LTR_elem, "W");
|
||||||
|
|
||||||
|
let ce_RTL_midPoint = getFirstCharPressPoint(doc, ce_RTL_elem, "א");
|
||||||
|
let tc_RTL_midPoint = getFirstCharPressPoint(doc, tc_RTL_elem, "ת");
|
||||||
|
let i_RTL_midPoint = getFirstCharPressPoint(doc, i_RTL_elem, "ל");
|
||||||
|
let ta_RTL_midPoint = getFirstCharPressPoint(doc, ta_RTL_elem, "ה");
|
||||||
|
|
||||||
|
|
||||||
|
// Longpress various LTR content elements. Test focused element against
|
||||||
|
// expected, and selected text against expected.
|
||||||
|
let result = yield do_promiseLongPressResult(ce_LTR_midPoint);
|
||||||
|
is(result.focusedElement, ce_LTR_elem, "Focused element should match expected.");
|
||||||
|
is(result.text, "Find", "Selected text should match expected text.");
|
||||||
|
|
||||||
|
result = yield do_promiseLongPressResult(tc_LTR_midPoint);
|
||||||
|
is(result.focusedElement, null, "No focused element is expected.");
|
||||||
|
is(result.text, "Open", "Selected text should match expected text.");
|
||||||
|
|
||||||
|
result = yield do_promiseLongPressResult(i_LTR_midPoint);
|
||||||
|
is(result.focusedElement, i_LTR_elem, "Focused element should match expected.");
|
||||||
|
is(result.text, "Type", "Selected text should match expected text.");
|
||||||
|
|
||||||
|
result = yield do_promiseLongPressResult(ta_LTR_midPoint);
|
||||||
|
is(result.focusedElement, ta_LTR_elem, "Focused element should match expected.");
|
||||||
|
is(result.text, "Words", "Selected text should match expected text.");
|
||||||
|
|
||||||
|
// Longpress various RTL content elements. Test focused element against
|
||||||
|
// expected, and selected text against expected.
|
||||||
|
result = yield do_promiseLongPressResult(ce_RTL_midPoint);
|
||||||
|
is(result.focusedElement, ce_RTL_elem, "Focused element should match expected.");
|
||||||
|
is(result.text, "איפה", "Selected text should match expected text.");
|
||||||
|
|
||||||
|
result = yield do_promiseLongPressResult(tc_RTL_midPoint);
|
||||||
|
is(result.focusedElement, null, "No focused element is expected.");
|
||||||
|
is(result.text, "תן", "Selected text should match expected text.");
|
||||||
|
|
||||||
|
result = yield do_promiseLongPressResult(i_RTL_midPoint);
|
||||||
|
is(result.focusedElement, i_RTL_elem, "Focused element should match expected.");
|
||||||
|
is(result.text, "לרוץ", "Selected text should match expected text.");
|
||||||
|
|
||||||
|
result = yield do_promiseLongPressResult(ta_RTL_midPoint);
|
||||||
|
is(result.focusedElement, ta_RTL_elem, "Focused element should match expected.");
|
||||||
|
is(result.text, "הספר", "Selected text should match expected text.");
|
||||||
|
|
||||||
|
ok(true, "Finished all tests.");
|
||||||
|
});
|
||||||
|
|
||||||
|
run_next_test();
|
Loading…
Reference in New Issue
Block a user