Bug 1060529, send the enabled state of child process commands to the parent on update, without the test, r=smaug,ehsan

This commit is contained in:
Neil Deakin 2014-12-09 10:48:27 -05:00
parent 9630089e11
commit 480511cee8
17 changed files with 292 additions and 36 deletions

View File

@ -9272,6 +9272,36 @@ nsGlobalWindow::ShowModalDialog(const nsAString& aURI, nsIVariant *aArgs_,
return rv.ErrorCode(); return rv.ErrorCode();
} }
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;
};
class CommandDispatcher : public nsRunnable class CommandDispatcher : public nsRunnable
{ {
public: public:
@ -9291,6 +9321,12 @@ public:
NS_IMETHODIMP NS_IMETHODIMP
nsGlobalWindow::UpdateCommands(const nsAString& anAction, nsISelection* aSel, int16_t aReason) nsGlobalWindow::UpdateCommands(const nsAString& anAction, nsISelection* aSel, int16_t aReason)
{ {
// 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));
return NS_OK;
}
nsPIDOMWindow *rootWindow = nsGlobalWindow::GetPrivateRoot(); nsPIDOMWindow *rootWindow = nsGlobalWindow::GetPrivateRoot();
if (!rootWindow) if (!rootWindow)
return NS_OK; return NS_OK;

View File

@ -15,8 +15,8 @@ class nsIControllers;
class nsIController; class nsIController;
#define NS_IWINDOWROOT_IID \ #define NS_IWINDOWROOT_IID \
{ 0x3f71f50c, 0xa7e0, 0x43bc, \ { 0x728a2682, 0x55c0, 0x4860, \
{ 0xac, 0x25, 0x4d, 0xbb, 0x88, 0x7b, 0x21, 0x09 } } { 0x82, 0x6b, 0x0c, 0x30, 0x0a, 0xac, 0xaa, 0x60 } }
class nsPIWindowRoot : public mozilla::dom::EventTarget class nsPIWindowRoot : public mozilla::dom::EventTarget
{ {
@ -33,6 +33,9 @@ public:
nsIController** aResult) = 0; nsIController** aResult) = 0;
virtual nsresult GetControllers(nsIControllers** 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 void SetParentTarget(mozilla::dom::EventTarget* aTarget) = 0;
virtual mozilla::dom::EventTarget* GetParentTarget() = 0; virtual mozilla::dom::EventTarget* GetParentTarget() = 0;
virtual nsIDOMWindow* GetOwnerGlobal() MOZ_OVERRIDE = 0; virtual nsIDOMWindow* GetOwnerGlobal() MOZ_OVERRIDE = 0;

View File

@ -281,6 +281,75 @@ nsWindowRoot::GetControllerForCommand(const char * aCommand,
return NS_OK; 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* nsIDOMNode*
nsWindowRoot::GetPopupNode() nsWindowRoot::GetPopupNode()
{ {

View File

@ -23,6 +23,8 @@ class EventChainPreVisitor;
#include "nsPIWindowRoot.h" #include "nsPIWindowRoot.h"
#include "nsCycleCollectionParticipant.h" #include "nsCycleCollectionParticipant.h"
#include "nsAutoPtr.h" #include "nsAutoPtr.h"
#include "nsTHashtable.h"
#include "nsHashKeys.h"
class nsWindowRoot : public nsPIWindowRoot class nsWindowRoot : public nsPIWindowRoot
{ {
@ -52,6 +54,9 @@ public:
virtual nsresult GetControllerForCommand(const char * aCommand, virtual nsresult GetControllerForCommand(const char * aCommand,
nsIController** _retval) MOZ_OVERRIDE; nsIController** _retval) MOZ_OVERRIDE;
virtual void GetEnabledDisabledCommands(nsTArray<nsCString>& aEnabledCommands,
nsTArray<nsCString>& aDisabledCommands) MOZ_OVERRIDE;
virtual nsIDOMNode* GetPopupNode() MOZ_OVERRIDE; virtual nsIDOMNode* GetPopupNode() MOZ_OVERRIDE;
virtual void SetPopupNode(nsIDOMNode* aNode) MOZ_OVERRIDE; virtual void SetPopupNode(nsIDOMNode* aNode) MOZ_OVERRIDE;
@ -72,6 +77,11 @@ public:
protected: protected:
virtual ~nsWindowRoot(); virtual ~nsWindowRoot();
void GetEnabledDisabledCommandsForControllers(nsIControllers* aControllers,
nsTHashtable<nsCharPtrHashKey>& aCommandsHandled,
nsTArray<nsCString>& aEnabledCommands,
nsTArray<nsCString>& aDisabledCommands);
// Members // Members
nsCOMPtr<nsPIDOMWindow> mWindow; nsCOMPtr<nsPIDOMWindow> mWindow;
// We own the manager, which owns event listeners attached to us. // We own the manager, which owns event listeners attached to us.

View File

@ -31,6 +31,7 @@ XPIDL_SOURCES += [
'nsIFrameRequestCallback.idl', 'nsIFrameRequestCallback.idl',
'nsIIdleObserver.idl', 'nsIIdleObserver.idl',
'nsIQueryContentEventResult.idl', 'nsIQueryContentEventResult.idl',
'nsIRemoteBrowser.idl',
'nsIServiceWorkerManager.idl', 'nsIServiceWorkerManager.idl',
'nsIStructuredCloneContainer.idl', 'nsIStructuredCloneContainer.idl',
'nsITabChild.idl', 'nsITabChild.idl',

View File

@ -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);
};

View File

@ -7,7 +7,10 @@
interface nsIContentFrameMessageManager; interface nsIContentFrameMessageManager;
interface nsIWebBrowserChrome3; 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 interface nsITabChild : nsISupports
{ {
readonly attribute nsIContentFrameMessageManager messageManager; readonly attribute nsIContentFrameMessageManager messageManager;
@ -15,5 +18,9 @@ interface nsITabChild : nsISupports
attribute nsIWebBrowserChrome3 webBrowserChrome; attribute nsIWebBrowserChrome3 webBrowserChrome;
[notxpcom] void sendRequestFocus(in boolean canFocus); [notxpcom] void sendRequestFocus(in boolean canFocus);
[noscript, notxpcom] void enableDisableCommands(in AString action,
in CommandsArrayRef enabledCommands,
in CommandsArrayRef disabledCommands);
}; };

View File

@ -244,6 +244,14 @@ parent:
*/ */
RequestFocus(bool canRaise); 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, prio(urgent) sync GetInputContext() returns (int32_t IMEEnabled,
int32_t IMEOpen, int32_t IMEOpen,
intptr_t NativeIMEContext); intptr_t NativeIMEContext);

View File

@ -3269,6 +3269,15 @@ TabChild::SendRequestFocus(bool aCanFocus)
PBrowserChild::SendRequestFocus(aCanFocus); PBrowserChild::SendRequestFocus(aCanFocus);
} }
void
TabChild::EnableDisableCommands(const nsAString& aAction,
nsTArray<nsCString>& aEnabledCommands,
nsTArray<nsCString>& aDisabledCommands)
{
PBrowserChild::SendEnableDisableCommands(PromiseFlatString(aAction),
aEnabledCommands, aDisabledCommands);
}
bool bool
TabChild::DoSendBlockingMessage(JSContext* aCx, TabChild::DoSendBlockingMessage(JSContext* aCx,
const nsAString& aMessage, const nsAString& aMessage,

View File

@ -47,6 +47,7 @@
#include "nsIWindowCreator2.h" #include "nsIWindowCreator2.h"
#include "nsIXULBrowserWindow.h" #include "nsIXULBrowserWindow.h"
#include "nsIXULWindow.h" #include "nsIXULWindow.h"
#include "nsIRemoteBrowser.h"
#include "nsViewManager.h" #include "nsViewManager.h"
#include "nsIWidget.h" #include "nsIWidget.h"
#include "nsIWindowWatcher.h" #include "nsIWindowWatcher.h"
@ -1471,6 +1472,37 @@ TabParent::RecvRequestFocus(const bool& aCanRaise)
return true; 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 nsIntPoint
TabParent::GetChildProcessOffset() TabParent::GetChildProcessOffset()
{ {

View File

@ -191,6 +191,9 @@ public:
const int32_t& aCause, const int32_t& aCause,
const int32_t& aFocusChange) MOZ_OVERRIDE; const int32_t& aFocusChange) MOZ_OVERRIDE;
virtual bool RecvRequestFocus(const bool& aCanRaise) 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 RecvSetCursor(const uint32_t& aValue, const bool& aForce) MOZ_OVERRIDE;
virtual bool RecvSetBackgroundColor(const nscolor& aValue) MOZ_OVERRIDE; virtual bool RecvSetBackgroundColor(const nscolor& aValue) MOZ_OVERRIDE;
virtual bool RecvSetStatus(const uint32_t& aType, const nsString& aStatus) MOZ_OVERRIDE; virtual bool RecvSetStatus(const uint32_t& aType, const nsString& aStatus) MOZ_OVERRIDE;

View File

@ -25,7 +25,7 @@ interface nsIController : nsISupports {
interface nsICommandParams; interface nsICommandParams;
[scriptable, uuid(EBE55080-C8A9-11D5-A73C-DD620D6E04BC)] [scriptable, uuid(EEC0B435-7F53-44FE-B00A-CF3EED65C01A)]
interface nsICommandController : nsISupports interface nsICommandController : nsISupports
{ {
@ -33,6 +33,8 @@ interface nsICommandController : nsISupports
void doCommandWithParams(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);
}; };

View File

@ -174,3 +174,10 @@ nsBaseCommandController::OnEvent(const char * aEventName)
NS_ENSURE_ARG_POINTER(aEventName); NS_ENSURE_ARG_POINTER(aEventName);
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP
nsBaseCommandController::GetSupportedCommands(uint32_t* aCount, char*** aCommands)
{
NS_ENSURE_STATE(mCommandTable);
return mCommandTable->GetSupportedCommands(aCount, aCommands);
}

View File

@ -188,6 +188,30 @@ nsControllerCommandTable::GetCommandState(const char *aCommandName, nsICommandPa
return commandHandler->GetCommandStateParams(aCommandName, aParams, aCommandRefCon); 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 nsresult
NS_NewControllerCommandTable(nsIControllerCommandTable** aResult) NS_NewControllerCommandTable(nsIControllerCommandTable** aResult)

View File

@ -18,7 +18,7 @@
* *
*/ */
[scriptable, uuid(d1a47834-6ad4-11d7-bfad-000393636592)] [scriptable, uuid(c847f90e-b8f3-49db-a4df-8867831f2800)]
interface nsIControllerCommandTable : nsISupports interface nsIControllerCommandTable : nsISupports
{ {
/** /**
@ -82,6 +82,9 @@ interface nsIControllerCommandTable : nsISupports
void doCommandParams(in string aCommandName, in nsICommandParams aParam, 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 getCommandState(in string aCommandName, in nsICommandParams aParam, in nsISupports aCommandRefCon);
void getSupportedCommands(out unsigned long count,
[array, size_is(count), retval] out string commands);
}; };

View File

@ -10,7 +10,8 @@
<binding id="remote-browser" extends="chrome://global/content/bindings/browser.xml#browser"> <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> <field name="_securityUI">null</field>
@ -372,6 +373,21 @@
</body> </body>
</method> </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> </implementation>
</binding> </binding>

View File

@ -14,45 +14,45 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
function RemoteController(browser) function RemoteController(browser)
{ {
this._browser = 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 = { RemoteController.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIController]), QueryInterface: XPCOMUtils.generateQI([Ci.nsIController]),
isCommandEnabled: function(aCommand) { isCommandEnabled: function(aCommand) {
// We can't synchronously ask content if a command is enabled, return this._supportedCommands[aCommand] || false;
// 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;
}, },
supportsCommand: function(aCommand) { supportsCommand: function(aCommand) {
// Optimize the lookup a bit. return aCommand in this._supportedCommands;
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;
}, },
doCommand: function(aCommand) { doCommand: function(aCommand) {
this._browser.messageManager.sendAsyncMessage("ControllerCommands:Do", 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);
}
}; };