summaryrefslogtreecommitdiffstats
path: root/devtools/client/framework/test/browser_toolbox_textbox_context_menu.js
blob: 903d0c99127c996a61e529c96704510d954fd74f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

// HTML inputs don't automatically get the 'edit' context menu, so we have
// a helper on the toolbox to do so. Make sure that shows menu items in the
// right state, and that it works for an input inside of a panel.

const URL = "data:text/html;charset=utf8,test for textbox context menu";
const textboxToolId = "testtool1";

registerCleanupFunction(() => {
  gDevTools.unregisterTool(textboxToolId);
});

add_task(async function checkMenuEntryStates() {
  info("Checking the state of edit menuitems with an empty clipboard");
  const toolbox = await openNewTabAndToolbox(URL, "inspector");

  emptyClipboard();

  // Make sure the focus is predictable.
  const inspector = toolbox.getPanel("inspector");
  const onFocus = once(inspector.searchBox, "focus");
  inspector.searchBox.focus();
  await onFocus;

  info("Opening context menu");
  const onContextMenuPopup = toolbox.once("menu-open");
  synthesizeContextMenuEvent(inspector.searchBox);
  await onContextMenuPopup;

  const textboxContextMenu = toolbox.getTextBoxContextMenu();
  ok(textboxContextMenu, "The textbox context menu is loaded in the toolbox");

  const cmdUndo = textboxContextMenu.querySelector("#editmenu-undo");
  const cmdDelete = textboxContextMenu.querySelector("#editmenu-delete");
  const cmdSelectAll = textboxContextMenu.querySelector("#editmenu-selectAll");
  const cmdCut = textboxContextMenu.querySelector("#editmenu-cut");
  const cmdCopy = textboxContextMenu.querySelector("#editmenu-copy");
  const cmdPaste = textboxContextMenu.querySelector("#editmenu-paste");

  is(cmdUndo.getAttribute("disabled"), "true", "cmdUndo is disabled");
  is(cmdDelete.getAttribute("disabled"), "true", "cmdDelete is disabled");
  is(cmdSelectAll.getAttribute("disabled"), "true", "cmdSelectAll is disabled");
  is(cmdCut.getAttribute("disabled"), "true", "cmdCut is disabled");
  is(cmdCopy.getAttribute("disabled"), "true", "cmdCopy is disabled");

  if (isWindows()) {
    // emptyClipboard only works on Windows (666254), assert paste only for this OS.
    is(cmdPaste.getAttribute("disabled"), "true", "cmdPaste is disabled");
  }

  const onContextMenuHidden = toolbox.once("menu-close");
  if (Services.prefs.getBoolPref("widget.macos.native-context-menus", false)) {
    info("Using hidePopup semantics because of macOS native context menus.");
    textboxContextMenu.hidePopup();
  } else {
    EventUtils.sendKey("ESCAPE", toolbox.win);
  }
  await onContextMenuHidden;
});

add_task(async function automaticallyBindTexbox() {
  info(
    "Registering a tool with an input field and making sure the context menu works"
  );
  gDevTools.registerTool({
    id: textboxToolId,
    isToolSupported: () => true,
    url: CHROME_URL_ROOT + "doc_textbox_tool.html",
    label: "Context menu works without tool intervention",
    build(iframeWindow, toolbox) {
      this.panel = createTestPanel(iframeWindow, toolbox);
      return this.panel.open();
    },
  });

  const toolbox = await openNewTabAndToolbox(URL, textboxToolId);
  is(toolbox.currentToolId, textboxToolId, "The custom tool has been opened");

  const doc = toolbox.getCurrentPanel().document;
  await checkTextBox(doc.querySelector("input[type=text]"), toolbox);
  await checkTextBox(doc.querySelector("textarea"), toolbox);
  await checkTextBox(doc.querySelector("input[type=search]"), toolbox);
  await checkTextBox(doc.querySelector("input:not([type])"), toolbox);
  await checkNonTextInput(doc.querySelector("input[type=radio]"), toolbox);
});

async function checkNonTextInput(input, toolbox) {
  let textboxContextMenu = toolbox.getTextBoxContextMenu();
  ok(!textboxContextMenu, "The menu is closed");

  info(
    "Simulating context click on the non text input and expecting no menu to open"
  );
  const eventBubbledUp = new Promise(resolve => {
    input.ownerDocument.addEventListener("contextmenu", resolve, {
      once: true,
    });
  });
  synthesizeContextMenuEvent(input);
  info("Waiting for event");
  await eventBubbledUp;

  textboxContextMenu = toolbox.getTextBoxContextMenu();
  ok(!textboxContextMenu, "The menu is still closed");
}

async function checkTextBox(textBox, toolbox) {
  let textboxContextMenu = toolbox.getTextBoxContextMenu();
  ok(!textboxContextMenu, "The menu is closed");

  info(
    "Simulating context click on the textbox and expecting the menu to open"
  );
  const onContextMenu = toolbox.once("menu-open");
  synthesizeContextMenuEvent(textBox);
  await onContextMenu;

  textboxContextMenu = toolbox.getTextBoxContextMenu();
  ok(textboxContextMenu, "The menu is now visible");

  info("Closing the menu");
  const onContextMenuHidden = toolbox.once("menu-close");
  if (Services.prefs.getBoolPref("widget.macos.native-context-menus", false)) {
    info("Using hidePopup semantics because of macOS native context menus.");
    textboxContextMenu.hidePopup();
  } else {
    EventUtils.sendKey("ESCAPE", toolbox.win);
  }
  await onContextMenuHidden;

  textboxContextMenu = toolbox.getTextBoxContextMenu();
  ok(!textboxContextMenu, "The menu is closed again");
}