--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -493,8 +493,10 @@ skip-if = e10s
[browser_addCertException.js]
skip-if = e10s # Bug ?????? - test directly manipulates content (content.document.getElementById)
[browser_bug1045809.js]
[browser_e10s_switchbrowser.js]
[browser_blockHPKP.js]
skip-if = e10s # bug ?????? - test directly manipulates content (content.document.getElementById)
[browser_mcb_redirect.js]
skip-if = e10s # bug 1084504 - [e10s] Mixed content detection does not take redirection into account
+[browser_updatecommands.js]
+skip-if = os == "win" && e10s # times out on Windows 7 e10s intermittently
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_updatecommands.js
@@ -0,0 +1,89 @@
+let testPage = "data:text/html,<body><input id='input1' value='value'><select size=2><option val=1>One</select></body>";
+
+let browser;
+
+function test() {
+ waitForExplicitFinish();
+
+ var tab = gBrowser.addTab();
+ browser = gBrowser.getBrowserForTab(tab);
+ gBrowser.selectedTab = tab;
+
+ function runFirstTest(event) {
+ browser.removeEventListener("load", runFirstTest, true);
+ addEventListener("commandupdate", checkTest, false);
+ doTest();
+ }
+
+ browser.addEventListener("load", runFirstTest, true);
+ browser.contentWindow.location = testPage;
+
+ gURLBar.focus();
+}
+
+let currentTest;
+
+let tests = [
+ // Switch focus to the content. The body should be focused and select all should be enabled. In a
+ // multi-process browser, we won't see the blur update.
+ { name: "focus body", test: function() { browser.focus() }, skipUpdate: gMultiProcessBrowser ? 0 : 1,
+ commands: { "cmd_copy" : false, "cmd_paste": false, "cmd_selectAll" : true, "cmd_undo" : false, "cmd_redo": false } },
+
+ // Switch focus to 'input1'. Paste and select all should be enabled.
+ { name: "focus input", test: function() { EventUtils.synthesizeKey("VK_TAB", {}) }, skipUpdate: 2,
+ commands: { "cmd_copy" : true, "cmd_paste": true, "cmd_selectAll" : true, "cmd_undo" : false, "cmd_redo": false } },
+
+ // Move cursor to end which will deselect the text. Copy should be disabled but paste and select all should still be enabled.
+ { name: "cursor right", test: function() { EventUtils.synthesizeKey("VK_RIGHT", {}) },
+ commands: { "cmd_copy" : false, "cmd_paste": true, "cmd_selectAll" : true, "cmd_undo" : false, "cmd_redo": false } },
+
+ // Select all of the text. Copy should become enabled.
+ { name: "select all", test: function() { EventUtils.synthesizeKey("a", { accelKey: true }) },
+ commands: { "cmd_copy" : true, "cmd_paste": true, "cmd_selectAll" : true, "cmd_undo" : false, "cmd_redo": false } },
+
+ // Replace the text with 'c'. Copy should now be disabled and undo enabled.
+ { name: "change value", test: function() { EventUtils.synthesizeKey("c", {}) },
+ commands: { "cmd_copy" : false, "cmd_paste": true, "cmd_selectAll" : true, "cmd_undo" : true, "cmd_redo": false } },
+
+ // Undo. Undo should be disabled and redo enabled. The text is reselected so copy is enabled.
+ { name: "undo", test: function() { EventUtils.synthesizeKey("z", {accelKey: true }) },
+ commands: { "cmd_copy" : true, "cmd_paste": true, "cmd_selectAll" : true, "cmd_undo" : false, "cmd_redo": true } },
+
+ // Switch focus to the select. Only select all should now be enabled.
+ { name: "focus select", test: function() { EventUtils.synthesizeKey("VK_TAB", {}) }, skipUpdate: 1,
+ commands: { "cmd_copy" : false, "cmd_paste": false, "cmd_selectAll" : true, "cmd_undo" : false, "cmd_redo": false } },
+];
+
+function doTest()
+{
+ if (!tests.length) {
+ removeEventListener("commandupdate", checkTest, false);
+ gBrowser.removeCurrentTab();
+ finish();
+ return;
+ }
+
+ currentTest = tests.shift();
+ currentTest.test();
+}
+
+function checkTest(event)
+{
+ // Skip events fired on command updaters other than the main edit menu one.
+ if (event.target != document.getElementById("editMenuCommandSetAll")) {
+ return;
+ }
+
+ // Updates also occur on blur. These should be ignored.
+ if ("skipUpdate" in currentTest && currentTest["skipUpdate"] > 0) {
+ currentTest["skipUpdate"]--;
+ return;
+ }
+
+ for (let command in currentTest.commands) {
+ is(document.getElementById(command).getAttribute("disabled") != "true", currentTest.commands[command],
+ currentTest["name"] + " " + command);
+ }
+
+ SimpleTest.executeSoon(doTest);
+}
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -9304,16 +9304,46 @@ public:
{
return mDispatcher->UpdateCommands(mAction);
}
nsCOMPtr<nsIDOMXULCommandDispatcher> mDispatcher;
nsString mAction;
};
+class ChildCommandDispatcher : public nsRunnable
+{
+public:
+ ChildCommandDispatcher(nsGlobalWindow* aWindow,
+ nsITabChild* aTabChild,
+ const nsAString& aAction)
+ : mWindow(aWindow), mTabChild(aTabChild), mAction(aAction) {}
+
+ NS_IMETHOD Run()
+ {
+ nsCOMPtr<nsPIWindowRoot> root = mWindow->GetTopWindowRoot();
+ if (!root) {
+ return NS_OK;
+ }
+
+ nsTArray<nsCString> enabledCommands, disabledCommands;
+ root->GetEnabledDisabledCommands(enabledCommands, disabledCommands);
+ if (enabledCommands.Length() || disabledCommands.Length()) {
+ mTabChild->EnableDisableCommands(mAction, enabledCommands, disabledCommands);
+ }
+
+ return NS_OK;
+ }
+
+private:
+ nsRefPtr<nsGlobalWindow> mWindow;
+ nsCOMPtr<nsITabChild> mTabChild;
+ nsString mAction;
+};
+
static bool
CheckReason(int16_t aReason, SelectionChangeReason aReasonType)
{
switch (aReasonType) {
case SelectionChangeReason::Drag:
return aReason & nsISelectionListener::DRAG_REASON;
case SelectionChangeReason::Mousedown:
return aReason & nsISelectionListener::MOUSEDOWN_REASON;
@@ -9362,36 +9392,45 @@ GetSelectionBoundingRect(Selection* aSel
}
return res;
}
NS_IMETHODIMP
nsGlobalWindow::UpdateCommands(const nsAString& anAction, nsISelection* aSel, int16_t aReason)
{
- nsPIDOMWindow *rootWindow = nsGlobalWindow::GetPrivateRoot();
- if (!rootWindow)
+ if (!anAction.EqualsLiteral("selectionchange")) {
+ // If this is a child process, redirect to the parent process.
+ if (nsCOMPtr<nsITabChild> child = do_GetInterface(GetDocShell())) {
+ nsContentUtils::AddScriptRunner(new ChildCommandDispatcher(this, child, anAction));
+ } else {
+ nsPIDOMWindow* rootWindow = nsGlobalWindow::GetPrivateRoot();
+ if (!rootWindow) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIDOMXULDocument> xulDoc =
+ do_QueryInterface(rootWindow->GetExtantDoc());
+ // See if we contain a XUL document.
+ if (xulDoc) {
+ // Retrieve the command dispatcher and call updateCommands on it.
+ nsCOMPtr<nsIDOMXULCommandDispatcher> xulCommandDispatcher;
+ xulDoc->GetCommandDispatcher(getter_AddRefs(xulCommandDispatcher));
+ if (xulCommandDispatcher) {
+ nsContentUtils::AddScriptRunner(new CommandDispatcher(xulCommandDispatcher,
+ anAction));
+ }
+ }
+ }
+
return NS_OK;
-
- nsCOMPtr<nsIDOMXULDocument> xulDoc =
- do_QueryInterface(rootWindow->GetExtantDoc());
- // See if we contain a XUL document.
- // selectionchange action is only used for mozbrowser, not for XUL. So we bypass
- // XUL command dispatch if anAction is "selectionchange".
- if (xulDoc && !anAction.EqualsLiteral("selectionchange")) {
- // Retrieve the command dispatcher and call updateCommands on it.
- nsCOMPtr<nsIDOMXULCommandDispatcher> xulCommandDispatcher;
- xulDoc->GetCommandDispatcher(getter_AddRefs(xulCommandDispatcher));
- if (xulCommandDispatcher) {
- nsContentUtils::AddScriptRunner(new CommandDispatcher(xulCommandDispatcher,
- anAction));
- }
- }
-
- if (gSelectionCaretPrefEnabled && mDoc && anAction.EqualsLiteral("selectionchange")) {
+ }
+
+ // XXXndeakin this code will be removed by bug 1090008.
+ if (gSelectionCaretPrefEnabled && mDoc) {
SelectionChangeEventInit init;
init.mBubbles = true;
if (aSel) {
bool isTouchCaretVisible = false;
bool isCollapsed = aSel->Collapsed();
nsIPresShell *shell = mDoc->GetShell();
if (shell) {
--- a/dom/base/nsPIWindowRoot.h
+++ b/dom/base/nsPIWindowRoot.h
@@ -10,34 +10,37 @@
#include "nsISupports.h"
#include "mozilla/dom/EventTarget.h"
class nsPIDOMWindow;
class nsIControllers;
class nsIController;
#define NS_IWINDOWROOT_IID \
-{ 0x3f71f50c, 0xa7e0, 0x43bc, \
- { 0xac, 0x25, 0x4d, 0xbb, 0x88, 0x7b, 0x21, 0x09 } }
+{ 0x728a2682, 0x55c0, 0x4860, \
+ { 0x82, 0x6b, 0x0c, 0x30, 0x0a, 0xac, 0xaa, 0x60 } }
class nsPIWindowRoot : public mozilla::dom::EventTarget
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IWINDOWROOT_IID)
virtual nsPIDOMWindow* GetWindow()=0;
// get and set the node that is the context of a popup menu
virtual nsIDOMNode* GetPopupNode() = 0;
virtual void SetPopupNode(nsIDOMNode* aNode) = 0;
virtual nsresult GetControllerForCommand(const char *aCommand,
nsIController** aResult) = 0;
virtual nsresult GetControllers(nsIControllers** aResult) = 0;
+ virtual void GetEnabledDisabledCommands(nsTArray<nsCString>& aEnabledCommands,
+ nsTArray<nsCString>& aDisabledCommands) = 0;
+
virtual void SetParentTarget(mozilla::dom::EventTarget* aTarget) = 0;
virtual mozilla::dom::EventTarget* GetParentTarget() = 0;
virtual nsIDOMWindow* GetOwnerGlobal() MOZ_OVERRIDE = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsPIWindowRoot, NS_IWINDOWROOT_IID)
#endif // nsPIWindowRoot_h__
--- a/dom/base/nsWindowRoot.cpp
+++ b/dom/base/nsWindowRoot.cpp
@@ -276,16 +276,85 @@ nsWindowRoot::GetControllerForCommand(co
// XXXndeakin P3 is this casting safe?
nsGlobalWindow *win = static_cast<nsGlobalWindow*>(focusedWindow.get());
focusedWindow = win->GetPrivateParent();
}
return NS_OK;
}
+void
+nsWindowRoot::GetEnabledDisabledCommandsForControllers(nsIControllers* aControllers,
+ nsTHashtable<nsCharPtrHashKey>& aCommandsHandled,
+ nsTArray<nsCString>& aEnabledCommands,
+ nsTArray<nsCString>& aDisabledCommands)
+{
+ uint32_t controllerCount;
+ aControllers->GetControllerCount(&controllerCount);
+ for (uint32_t c = 0; c < controllerCount; c++) {
+ nsCOMPtr<nsIController> controller;
+ aControllers->GetControllerAt(c, getter_AddRefs(controller));
+
+ nsCOMPtr<nsICommandController> commandController(do_QueryInterface(controller));
+ if (commandController) {
+ uint32_t commandsCount;
+ char** commands;
+ if (NS_SUCCEEDED(commandController->GetSupportedCommands(&commandsCount, &commands))) {
+ for (uint32_t e = 0; e < commandsCount; e++) {
+ // Use a hash to determine which commands have already been handled by
+ // earlier controllers, as the earlier controller's result should get
+ // priority.
+ if (!aCommandsHandled.Contains(commands[e])) {
+ aCommandsHandled.PutEntry(commands[e]);
+
+ bool enabled = false;
+ controller->IsCommandEnabled(commands[e], &enabled);
+
+ const nsDependentCSubstring commandStr(commands[e], strlen(commands[e]));
+ if (enabled) {
+ aEnabledCommands.AppendElement(commandStr);
+ } else {
+ aDisabledCommands.AppendElement(commandStr);
+ }
+ }
+ }
+
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(commandsCount, commands);
+ }
+ }
+ }
+}
+
+void
+nsWindowRoot::GetEnabledDisabledCommands(nsTArray<nsCString>& aEnabledCommands,
+ nsTArray<nsCString>& aDisabledCommands)
+{
+ nsTHashtable<nsCharPtrHashKey> commandsHandled;
+
+ nsCOMPtr<nsIControllers> controllers;
+ GetControllers(getter_AddRefs(controllers));
+ if (controllers) {
+ GetEnabledDisabledCommandsForControllers(controllers, commandsHandled,
+ aEnabledCommands, aDisabledCommands);
+ }
+
+ nsCOMPtr<nsPIDOMWindow> focusedWindow;
+ nsFocusManager::GetFocusedDescendant(mWindow, true, getter_AddRefs(focusedWindow));
+ while (focusedWindow) {
+ focusedWindow->GetControllers(getter_AddRefs(controllers));
+ if (controllers) {
+ GetEnabledDisabledCommandsForControllers(controllers, commandsHandled,
+ aEnabledCommands, aDisabledCommands);
+ }
+
+ nsGlobalWindow* win = static_cast<nsGlobalWindow*>(focusedWindow.get());
+ focusedWindow = win->GetPrivateParent();
+ }
+}
+
nsIDOMNode*
nsWindowRoot::GetPopupNode()
{
return mPopupNode;
}
void
nsWindowRoot::SetPopupNode(nsIDOMNode* aNode)
--- a/dom/base/nsWindowRoot.h
+++ b/dom/base/nsWindowRoot.h
@@ -18,16 +18,18 @@ class EventChainPreVisitor;
} // namespace mozilla
#include "mozilla/Attributes.h"
#include "mozilla/EventListenerManager.h"
#include "nsIDOMEventTarget.h"
#include "nsPIWindowRoot.h"
#include "nsCycleCollectionParticipant.h"
#include "nsAutoPtr.h"
+#include "nsTHashtable.h"
+#include "nsHashKeys.h"
class nsWindowRoot : public nsPIWindowRoot
{
public:
explicit nsWindowRoot(nsPIDOMWindow* aWindow);
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_NSIDOMEVENTTARGET
@@ -47,16 +49,19 @@ public:
// nsPIWindowRoot
virtual nsPIDOMWindow* GetWindow() MOZ_OVERRIDE;
virtual nsresult GetControllers(nsIControllers** aResult) MOZ_OVERRIDE;
virtual nsresult GetControllerForCommand(const char * aCommand,
nsIController** _retval) MOZ_OVERRIDE;
+ virtual void GetEnabledDisabledCommands(nsTArray<nsCString>& aEnabledCommands,
+ nsTArray<nsCString>& aDisabledCommands) MOZ_OVERRIDE;
+
virtual nsIDOMNode* GetPopupNode() MOZ_OVERRIDE;
virtual void SetPopupNode(nsIDOMNode* aNode) MOZ_OVERRIDE;
virtual void SetParentTarget(mozilla::dom::EventTarget* aTarget) MOZ_OVERRIDE
{
mParent = aTarget;
}
virtual mozilla::dom::EventTarget* GetParentTarget() MOZ_OVERRIDE { return mParent; }
@@ -67,16 +72,21 @@ public:
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsWindowRoot,
nsIDOMEventTarget)
protected:
virtual ~nsWindowRoot();
+ void GetEnabledDisabledCommandsForControllers(nsIControllers* aControllers,
+ nsTHashtable<nsCharPtrHashKey>& aCommandsHandled,
+ nsTArray<nsCString>& aEnabledCommands,
+ nsTArray<nsCString>& aDisabledCommands);
+
// Members
nsCOMPtr<nsPIDOMWindow> mWindow;
// We own the manager, which owns event listeners attached to us.
nsRefPtr<mozilla::EventListenerManager> mListenerManager; // [Strong]
nsCOMPtr<nsIDOMNode> mPopupNode; // [OWNER]
nsCOMPtr<mozilla::dom::EventTarget> mParent;
};
--- a/dom/interfaces/base/moz.build
+++ b/dom/interfaces/base/moz.build
@@ -27,16 +27,17 @@ XPIDL_SOURCES += [
'nsIDOMScreen.idl',
'nsIDOMWindow.idl',
'nsIDOMWindowCollection.idl',
'nsIDOMWindowUtils.idl',
'nsIFocusManager.idl',
'nsIFrameRequestCallback.idl',
'nsIIdleObserver.idl',
'nsIQueryContentEventResult.idl',
+ 'nsIRemoteBrowser.idl',
'nsIServiceWorkerManager.idl',
'nsIStructuredCloneContainer.idl',
'nsITabChild.idl',
'nsITabParent.idl',
]
XPIDL_MODULE = 'dom_base'
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/base/nsIRemoteBrowser.idl
@@ -0,0 +1,26 @@
+/* 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/. */
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(C8379366-F79F-4D25-89A6-22BEC0A93D16)]
+interface nsIRemoteBrowser : nsISupports
+{
+ /*
+ * Called by the child to inform the parent that a command update has occurred
+ * and the supplied set of commands are now enabled and disabled.
+ *
+ * @param action command updater action
+ * @param enabledLength length of enabledCommands array
+ * @param enabledCommands commands to enable
+ * @param disabledLength length of disabledCommands array
+ * @param disabledCommand commands to disable
+ */
+ void enableDisableCommands(in AString action,
+ in unsigned long enabledLength,
+ [array, size_is(enabledLength)] in string enabledCommands,
+ in unsigned long disabledLength,
+ [array, size_is(disabledLength)] in string disabledCommands);
+};
+
--- a/dom/interfaces/base/nsITabChild.idl
+++ b/dom/interfaces/base/nsITabChild.idl
@@ -2,18 +2,25 @@
* 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/. */
#include "domstubs.idl"
interface nsIContentFrameMessageManager;
interface nsIWebBrowserChrome3;
-[scriptable, uuid(2eb3bc54-78bf-40f2-b301-a5b5b70f7da0)]
+native CommandsArray(nsTArray<nsCString>);
+[ref] native CommandsArrayRef(nsTArray<nsCString>);
+
+[scriptable, uuid(7227bac4-b6fe-4090-aeb4-bc288b790925)]
interface nsITabChild : nsISupports
{
readonly attribute nsIContentFrameMessageManager messageManager;
attribute nsIWebBrowserChrome3 webBrowserChrome;
[notxpcom] void sendRequestFocus(in boolean canFocus);
+
+ [noscript, notxpcom] void enableDisableCommands(in AString action,
+ in CommandsArrayRef enabledCommands,
+ in CommandsArrayRef disabledCommands);
};
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -215,16 +215,24 @@ parent:
prio(urgent) sync EndIMEComposition(bool cancel) returns (nsString composition);
/**
* Request that the parent process move focus to the browser's frame. If
* canRaise is true, the window can be raised if it is inactive.
*/
RequestFocus(bool canRaise);
+ /**
+ * Indicate, based on the current state, that some commands are enabled and
+ * some are disabled.
+ */
+ EnableDisableCommands(nsString action,
+ nsCString[] enabledCommands,
+ nsCString[] disabledCommands);
+
prio(urgent) sync GetInputContext() returns (int32_t IMEEnabled,
int32_t IMEOpen,
intptr_t NativeIMEContext);
prio(urgent) async SetInputContext(int32_t IMEEnabled,
int32_t IMEOpen,
nsString type,
nsString inputmode,
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -2989,16 +2989,25 @@ TabChild::SetWebBrowserChrome(nsIWebBrow
}
void
TabChild::SendRequestFocus(bool aCanFocus)
{
PBrowserChild::SendRequestFocus(aCanFocus);
}
+void
+TabChild::EnableDisableCommands(const nsAString& aAction,
+ nsTArray<nsCString>& aEnabledCommands,
+ nsTArray<nsCString>& aDisabledCommands)
+{
+ PBrowserChild::SendEnableDisableCommands(PromiseFlatString(aAction),
+ aEnabledCommands, aDisabledCommands);
+}
+
bool
TabChild::DoSendBlockingMessage(JSContext* aCx,
const nsAString& aMessage,
const StructuredCloneData& aData,
JS::Handle<JSObject *> aCpows,
nsIPrincipal* aPrincipal,
InfallibleTArray<nsString>* aJSONRetVal,
bool aIsSync)
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -42,16 +42,17 @@
#include "nsIInterfaceRequestorUtils.h"
#include "nsILoadInfo.h"
#include "nsIPromptFactory.h"
#include "nsIURI.h"
#include "nsIWebBrowserChrome.h"
#include "nsIWindowCreator2.h"
#include "nsIXULBrowserWindow.h"
#include "nsIXULWindow.h"
+#include "nsIRemoteBrowser.h"
#include "nsViewManager.h"
#include "nsIWidget.h"
#include "nsIWindowWatcher.h"
#include "nsOpenURIInFrameParams.h"
#include "nsPIDOMWindow.h"
#include "nsPIWindowWatcher.h"
#include "nsPresShell.h"
#include "nsPrintfCString.h"
@@ -1430,16 +1431,47 @@ TabParent::RecvRequestFocus(const bool&
if (aCanRaise)
flags |= nsIFocusManager::FLAG_RAISE;
nsCOMPtr<nsIDOMElement> node = do_QueryInterface(mFrameElement);
fm->SetFocus(node, flags);
return true;
}
+bool
+TabParent::RecvEnableDisableCommands(const nsString& aAction,
+ const nsTArray<nsCString>& aEnabledCommands,
+ const nsTArray<nsCString>& aDisabledCommands)
+{
+ nsCOMPtr<nsIRemoteBrowser> remoteBrowser = do_QueryInterface(mFrameElement);
+ if (remoteBrowser) {
+ nsAutoArrayPtr<const char*> enabledCommands, disabledCommands;
+
+ if (aEnabledCommands.Length()) {
+ enabledCommands = new const char* [aEnabledCommands.Length()];
+ for (uint32_t c = 0; c < aEnabledCommands.Length(); c++) {
+ enabledCommands[c] = aEnabledCommands[c].get();
+ }
+ }
+
+ if (aDisabledCommands.Length()) {
+ disabledCommands = new const char* [aDisabledCommands.Length()];
+ for (uint32_t c = 0; c < aDisabledCommands.Length(); c++) {
+ disabledCommands[c] = aDisabledCommands[c].get();
+ }
+ }
+
+ remoteBrowser->EnableDisableCommands(aAction,
+ aEnabledCommands.Length(), enabledCommands,
+ aDisabledCommands.Length(), disabledCommands);
+ }
+
+ return true;
+}
+
nsIntPoint
TabParent::GetChildProcessOffset()
{
// The "toplevel widget" in child processes is always at position
// 0,0. Map the event coordinates to match that.
nsIntPoint offset(0, 0);
nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -189,16 +189,19 @@ public:
virtual bool RecvSetInputContext(const int32_t& aIMEEnabled,
const int32_t& aIMEOpen,
const nsString& aType,
const nsString& aInputmode,
const nsString& aActionHint,
const int32_t& aCause,
const int32_t& aFocusChange) MOZ_OVERRIDE;
virtual bool RecvRequestFocus(const bool& aCanRaise) MOZ_OVERRIDE;
+ virtual bool RecvEnableDisableCommands(const nsString& aAction,
+ const nsTArray<nsCString>& aEnabledCommands,
+ const nsTArray<nsCString>& aDisabledCommands) MOZ_OVERRIDE;
virtual bool RecvSetCursor(const uint32_t& aValue, const bool& aForce) MOZ_OVERRIDE;
virtual bool RecvSetBackgroundColor(const nscolor& aValue) MOZ_OVERRIDE;
virtual bool RecvSetStatus(const uint32_t& aType, const nsString& aStatus) MOZ_OVERRIDE;
virtual bool RecvIsParentWindowMainWidgetVisible(bool* aIsVisible);
virtual bool RecvShowTooltip(const uint32_t& aX, const uint32_t& aY, const nsString& aTooltip);
virtual bool RecvHideTooltip();
virtual bool RecvGetDPI(float* aValue) MOZ_OVERRIDE;
virtual bool RecvGetDefaultScale(double* aValue) MOZ_OVERRIDE;
--- a/dom/xul/nsIController.idl
+++ b/dom/xul/nsIController.idl
@@ -2,42 +2,44 @@
/* 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/. */
#include "nsISupports.idl"
[scriptable, uuid(D5B61B82-1DA4-11d3-BF87-00105A1B0627)]
interface nsIController : nsISupports {
- boolean isCommandEnabled(in string command);
- boolean supportsCommand(in string command);
+ boolean isCommandEnabled(in string command);
+ boolean supportsCommand(in string command);
- void doCommand(in string command);
+ void doCommand(in string command);
- void onEvent(in string eventName);
+ void onEvent(in string eventName);
};
/*
Enhanced controller interface that allows for passing of parameters
to commands.
*/
interface nsICommandParams;
-[scriptable, uuid(EBE55080-C8A9-11D5-A73C-DD620D6E04BC)]
+[scriptable, uuid(EEC0B435-7F53-44FE-B00A-CF3EED65C01A)]
interface nsICommandController : nsISupports
{
void getCommandStateWithParams( in string command, in nsICommandParams aCommandParams);
void doCommandWithParams(in string command, in nsICommandParams aCommandParams);
+ void getSupportedCommands(out unsigned long count,
+ [array, size_is(count), retval] out string commands);
};
/*
An API for registering commands in groups, to allow for
updating via nsIDOMWindow::UpdateCommands.
*/
interface nsISimpleEnumerator;
--- a/embedding/components/commandhandler/nsBaseCommandController.cpp
+++ b/embedding/components/commandhandler/nsBaseCommandController.cpp
@@ -169,8 +169,15 @@ nsBaseCommandController::GetCommandState
}
NS_IMETHODIMP
nsBaseCommandController::OnEvent(const char * aEventName)
{
NS_ENSURE_ARG_POINTER(aEventName);
return NS_OK;
}
+
+NS_IMETHODIMP
+nsBaseCommandController::GetSupportedCommands(uint32_t* aCount, char*** aCommands)
+{
+ NS_ENSURE_STATE(mCommandTable);
+ return mCommandTable->GetSupportedCommands(aCount, aCommands);
+}
--- a/embedding/components/commandhandler/nsControllerCommandTable.cpp
+++ b/embedding/components/commandhandler/nsControllerCommandTable.cpp
@@ -183,16 +183,40 @@ nsControllerCommandTable::GetCommandStat
#if DEBUG
NS_WARNING("Controller command table asked to do a command that it does not handle -- ");
#endif
return NS_OK; // we don't handle this command
}
return commandHandler->GetCommandStateParams(aCommandName, aParams, aCommandRefCon);
}
+static PLDHashOperator
+AddCommand(const nsACString& aKey, nsIControllerCommand* aData, void* aArg)
+{
+ // aArg is a pointer to a array of strings. It gets incremented after
+ // allocating each one so that it points to the next location for AddCommand
+ // to assign a string to.
+ char*** commands = static_cast<char***>(aArg);
+ (**commands) = ToNewCString(aKey);
+ (*commands)++;
+ return PL_DHASH_NEXT;
+}
+
+NS_IMETHODIMP
+nsControllerCommandTable::GetSupportedCommands(uint32_t* aCount,
+ char*** aCommands)
+{
+ char** commands =
+ static_cast<char **>(NS_Alloc(sizeof(char *) * mCommandsTable.Count()));
+ *aCount = mCommandsTable.Count();
+ *aCommands = commands;
+
+ mCommandsTable.EnumerateRead(AddCommand, &commands);
+ return NS_OK;
+}
nsresult
NS_NewControllerCommandTable(nsIControllerCommandTable** aResult)
{
NS_PRECONDITION(aResult != nullptr, "null ptr");
if (! aResult)
return NS_ERROR_NULL_POINTER;
--- a/embedding/components/commandhandler/nsIControllerCommandTable.idl
+++ b/embedding/components/commandhandler/nsIControllerCommandTable.idl
@@ -13,17 +13,17 @@
* and efficiently dispatch commands to their respective handlers.
*
* Controllers that use an nsIControllerCommandTable should support
* nsIInterfaceRequestor, and be able to return an interface to their
* controller command table via getInterface().
*
*/
-[scriptable, uuid(d1a47834-6ad4-11d7-bfad-000393636592)]
+[scriptable, uuid(c847f90e-b8f3-49db-a4df-8867831f2800)]
interface nsIControllerCommandTable : nsISupports
{
/**
* Make this command table immutable, so that commands cannot
* be registered or unregistered. Some command tables are made
* mutable after command registration so that they can be
* used as singletons.
*/
@@ -77,16 +77,19 @@ interface nsIControllerCommandTable : ns
* @param aCommandName the name of the command to execute
* @param aCommandRefCon the command context data
*/
void doCommand(in string aCommandName, in nsISupports aCommandRefCon);
void doCommandParams(in string aCommandName, in nsICommandParams aParam, in nsISupports aCommandRefCon);
void getCommandState(in string aCommandName, in nsICommandParams aParam, in nsISupports aCommandRefCon);
+
+ void getSupportedCommands(out unsigned long count,
+ [array, size_is(count), retval] out string commands);
};
%{C++
// {670ee5da-6ad5-11d7-9950-000393636592}
#define NS_CONTROLLERCOMMANDTABLE_CID \
{0x670ee5da, 0x6ad5, 0x11d7, \
--- a/toolkit/content/widgets/remote-browser.xml
+++ b/toolkit/content/widgets/remote-browser.xml
@@ -5,17 +5,18 @@
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<bindings id="firefoxBrowserBindings"
xmlns="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<binding id="remote-browser" extends="chrome://global/content/bindings/browser.xml#browser">
- <implementation type="application/javascript" implements="nsIObserver, nsIDOMEventListener, nsIMessageListener">
+ <implementation type="application/javascript"
+ implements="nsIObserver, nsIDOMEventListener, nsIMessageListener, nsIRemoteBrowser">
<field name="_securityUI">null</field>
<property name="securityUI"
readonly="true">
<getter><![CDATA[
if (!this._securityUI) {
let jsm = "resource://gre/modules/RemoteSecurityUI.jsm";
@@ -367,13 +368,28 @@
<body>
<![CDATA[
return { x: aScreenX + this.boxObject.screenX,
y: aScreenY + this.boxObject.screenY };
]]>
</body>
</method>
+ <method name="enableDisableCommands">
+ <parameter name="aAction"/>
+ <parameter name="aEnabledLength"/>
+ <parameter name="aEnabledCommands"/>
+ <parameter name="aDisabledLength"/>
+ <parameter name="aDisabledCommands"/>
+ <body>
+ if (this._controller) {
+ this._controller.enableDisableCommands(aAction,
+ aEnabledLength, aEnabledCommands,
+ aDisabledLength, aDisabledCommands);
+ }
+ </body>
+ </method>
+
</implementation>
</binding>
</bindings>
--- a/toolkit/modules/RemoteController.jsm
+++ b/toolkit/modules/RemoteController.jsm
@@ -9,50 +9,50 @@ const Ci = Components.interfaces;
const Cc = Components.classes;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
function RemoteController(browser)
{
this._browser = browser;
+
+ // A map of commands that have had their enabled/disabled state assigned. The
+ // value of each key will be true if enabled, and false if disabled.
+ this._supportedCommands = { };
}
RemoteController.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIController]),
isCommandEnabled: function(aCommand) {
- // We can't synchronously ask content if a command is enabled,
- // so we always pretend is.
- // The right way forward would be to never use nsIController
- // to ask if something in content is enabled. Maybe even
- // by replacing the nsIController architecture by something else.
- // See bug 905768.
- return true;
+ return this._supportedCommands[aCommand] || false;
},
supportsCommand: function(aCommand) {
- // Optimize the lookup a bit.
- if (!aCommand.startsWith("cmd_"))
- return false;
-
- // For now only support the commands used in "browser-context.inc"
- let commands = [
- "cmd_copyLink",
- "cmd_copyImage",
- "cmd_undo",
- "cmd_cut",
- "cmd_copy",
- "cmd_paste",
- "cmd_delete",
- "cmd_selectAll",
- "cmd_switchTextDirection"
- ];
-
- return commands.indexOf(aCommand) >= 0;
+ return aCommand in this._supportedCommands;
},
doCommand: function(aCommand) {
this._browser.messageManager.sendAsyncMessage("ControllerCommands:Do", aCommand);
},
- onEvent: function () {}
+ onEvent: function () {},
+
+ // This is intended to be called from the remote-browser binding to update
+ // the enabled and disabled commands.
+ enableDisableCommands: function(aAction,
+ aEnabledLength, aEnabledCommands,
+ aDisabledLength, aDisabledCommands) {
+ // Clear the list first
+ this._supportedCommands = { };
+
+ for (let c = 0; c < aEnabledLength; c++) {
+ this._supportedCommands[aEnabledCommands[c]] = true;
+ }
+
+ for (let c = 0; c < aDisabledLength; c++) {
+ this._supportedCommands[aDisabledCommands[c]] = false;
+ }
+
+ this._browser.ownerDocument.defaultView.updateCommands(aAction);
+ }
};