зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1650637 - Part3: Introduce WinRemoteMessageSender/Receiver. r=mossop
This patch introduces two classes WinRemoteMessageSender/Receiver to generate/parse a payload of WM_COPYDATA message which is sent from nsWinRemoteClient to nsWinRemoteServer. And, we change the encoding of the payload from utf-8 to utf-16, incrementing the message version from 1 to 2, because our parsing logic on nsWinRemoteServer does not support a variable-width character encoding like utf-8. Differential Revision: https://phabricator.services.mozilla.com/D84228
This commit is contained in:
Родитель
e08d50175e
Коммит
30b7f20b67
|
@ -0,0 +1,129 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */
|
||||
|
||||
#include "nsCommandLine.h"
|
||||
#include "mozilla/CmdLineAndEnvUtils.h"
|
||||
#include "WinRemoteMessage.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
WinRemoteMessageSender::WinRemoteMessageSender(const char* aCommandLine)
|
||||
: mData({static_cast<DWORD>(WinRemoteMessageVersion::CommandLineOnly)}),
|
||||
mUtf8Buffer(aCommandLine) {
|
||||
mUtf8Buffer.Append('\0');
|
||||
|
||||
char* mutableBuffer;
|
||||
mData.cbData = mUtf8Buffer.GetMutableData(&mutableBuffer);
|
||||
mData.lpData = mutableBuffer;
|
||||
}
|
||||
|
||||
WinRemoteMessageSender::WinRemoteMessageSender(const char* aCommandLine,
|
||||
const char* aWorkingDir)
|
||||
: mData({static_cast<DWORD>(
|
||||
WinRemoteMessageVersion::CommandLineAndWorkingDir)}),
|
||||
mUtf8Buffer(aCommandLine) {
|
||||
mUtf8Buffer.Append('\0');
|
||||
mUtf8Buffer.Append(aWorkingDir);
|
||||
mUtf8Buffer.Append('\0');
|
||||
|
||||
char* mutableBuffer;
|
||||
mData.cbData = mUtf8Buffer.GetMutableData(&mutableBuffer);
|
||||
mData.lpData = mutableBuffer;
|
||||
}
|
||||
|
||||
WinRemoteMessageSender::WinRemoteMessageSender(const wchar_t* aCommandLine,
|
||||
const wchar_t* aWorkingDir)
|
||||
: mData({static_cast<DWORD>(
|
||||
WinRemoteMessageVersion::CommandLineAndWorkingDirInUtf16)}),
|
||||
mUtf16Buffer(aCommandLine) {
|
||||
mUtf16Buffer.Append(u'\0');
|
||||
mUtf16Buffer.Append(aWorkingDir);
|
||||
mUtf16Buffer.Append(u'\0');
|
||||
|
||||
char16_t* mutableBuffer;
|
||||
mData.cbData = mUtf16Buffer.GetMutableData(&mutableBuffer) * sizeof(char16_t);
|
||||
mData.lpData = mutableBuffer;
|
||||
}
|
||||
|
||||
COPYDATASTRUCT* WinRemoteMessageSender::CopyData() { return &mData; }
|
||||
|
||||
nsresult WinRemoteMessageReceiver::ParseV0(char* aBuffer) {
|
||||
CommandLineParserWin<char> parser;
|
||||
parser.HandleCommandLine(aBuffer);
|
||||
|
||||
mCommandLine = new nsCommandLine();
|
||||
return mCommandLine->Init(parser.Argc(), parser.Argv(), nullptr,
|
||||
nsICommandLine::STATE_REMOTE_AUTO);
|
||||
}
|
||||
|
||||
nsresult WinRemoteMessageReceiver::ParseV1(char* aBuffer) {
|
||||
CommandLineParserWin<char> parser;
|
||||
parser.HandleCommandLine(aBuffer);
|
||||
|
||||
// Moving |wdpath| to the working dir followed by the first null char.
|
||||
char* wdpath = aBuffer;
|
||||
while (*wdpath) {
|
||||
++wdpath;
|
||||
}
|
||||
++wdpath;
|
||||
|
||||
nsCOMPtr<nsIFile> workingDir;
|
||||
NS_NewLocalFile(NS_ConvertUTF8toUTF16(wdpath), false,
|
||||
getter_AddRefs(workingDir));
|
||||
|
||||
mCommandLine = new nsCommandLine();
|
||||
return mCommandLine->Init(parser.Argc(), parser.Argv(), workingDir,
|
||||
nsICommandLine::STATE_REMOTE_AUTO);
|
||||
}
|
||||
|
||||
nsresult WinRemoteMessageReceiver::ParseV2(char16_t* aBuffer) {
|
||||
CommandLineParserWin<char16_t> parser;
|
||||
parser.HandleCommandLine(aBuffer);
|
||||
|
||||
// Moving |wdpath| to the working dir followed by the first null char.
|
||||
char16_t* wdpath = aBuffer;
|
||||
while (*wdpath) {
|
||||
++wdpath;
|
||||
}
|
||||
++wdpath;
|
||||
|
||||
nsCOMPtr<nsIFile> workingDir;
|
||||
NS_NewLocalFile(nsDependentString(wdpath), false, getter_AddRefs(workingDir));
|
||||
|
||||
int argc = parser.Argc();
|
||||
|
||||
Vector<nsAutoCString> utf8args;
|
||||
if (!utf8args.reserve(argc)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
UniquePtr<const char*[]> argv(new const char*[argc]);
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
utf8args.infallibleAppend(NS_ConvertUTF16toUTF8(parser.Argv()[i]));
|
||||
argv[i] = utf8args[i].get();
|
||||
}
|
||||
|
||||
mCommandLine = new nsCommandLine();
|
||||
return mCommandLine->Init(argc, argv.get(), workingDir,
|
||||
nsICommandLine::STATE_REMOTE_AUTO);
|
||||
}
|
||||
|
||||
nsresult WinRemoteMessageReceiver::Parse(COPYDATASTRUCT* aMessageData) {
|
||||
switch (static_cast<WinRemoteMessageVersion>(aMessageData->dwData)) {
|
||||
case WinRemoteMessageVersion::CommandLineOnly:
|
||||
return ParseV0(reinterpret_cast<char*>(aMessageData->lpData));
|
||||
case WinRemoteMessageVersion::CommandLineAndWorkingDir:
|
||||
return ParseV1(reinterpret_cast<char*>(aMessageData->lpData));
|
||||
case WinRemoteMessageVersion::CommandLineAndWorkingDirInUtf16:
|
||||
return ParseV2(reinterpret_cast<char16_t*>(aMessageData->lpData));
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Unsupported message version");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
nsICommandLineRunner* WinRemoteMessageReceiver::CommandLineRunner() {
|
||||
return mCommandLine;
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */
|
||||
|
||||
#ifndef __WinRemoteMessage_h__
|
||||
#define __WinRemoteMessage_h__
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "nsICommandLineRunner.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
// This version defines the format of COPYDATASTRUCT::lpData in a message of
|
||||
// WM_COPYDATA.
|
||||
// Always use the latest version for production use because the older versions
|
||||
// have a bug that a non-ascii character in a utf-8 message cannot be parsed
|
||||
// correctly (bug 1650637). We keep the older versions for backward
|
||||
// compatibility and the testing purpose only.
|
||||
enum class WinRemoteMessageVersion : uint32_t {
|
||||
// "<CommandLine>\0" in utf8
|
||||
CommandLineOnly = 0,
|
||||
// "<CommandLine>\0<WorkingDir>\0" in utf8
|
||||
CommandLineAndWorkingDir,
|
||||
// L"<CommandLine>\0<WorkingDir>\0" in utf16
|
||||
CommandLineAndWorkingDirInUtf16,
|
||||
};
|
||||
|
||||
class WinRemoteMessageSender final {
|
||||
COPYDATASTRUCT mData;
|
||||
nsAutoString mUtf16Buffer;
|
||||
nsAutoCString mUtf8Buffer;
|
||||
|
||||
public:
|
||||
WinRemoteMessageSender(const wchar_t* aCommandLine,
|
||||
const wchar_t* aWorkingDir);
|
||||
|
||||
WinRemoteMessageSender(const WinRemoteMessageSender&) = delete;
|
||||
WinRemoteMessageSender(WinRemoteMessageSender&&) = delete;
|
||||
WinRemoteMessageSender& operator=(const WinRemoteMessageSender&) = delete;
|
||||
WinRemoteMessageSender& operator=(WinRemoteMessageSender&&) = delete;
|
||||
|
||||
COPYDATASTRUCT* CopyData();
|
||||
|
||||
// Constructors for the old formats. Testing purpose only.
|
||||
explicit WinRemoteMessageSender(const char* aCommandLine);
|
||||
WinRemoteMessageSender(const char* aCommandLine, const char* aWorkingDir);
|
||||
};
|
||||
|
||||
class WinRemoteMessageReceiver final {
|
||||
nsCOMPtr<nsICommandLineRunner> mCommandLine;
|
||||
|
||||
nsresult ParseV0(char* aBuffer);
|
||||
nsresult ParseV1(char* aBuffer);
|
||||
nsresult ParseV2(char16_t* aBuffer);
|
||||
|
||||
public:
|
||||
WinRemoteMessageReceiver() = default;
|
||||
WinRemoteMessageReceiver(const WinRemoteMessageReceiver&) = delete;
|
||||
WinRemoteMessageReceiver(WinRemoteMessageReceiver&&) = delete;
|
||||
WinRemoteMessageReceiver& operator=(const WinRemoteMessageReceiver&) = delete;
|
||||
WinRemoteMessageReceiver& operator=(WinRemoteMessageReceiver&&) = delete;
|
||||
|
||||
nsresult Parse(COPYDATASTRUCT* aMessageData);
|
||||
nsICommandLineRunner* CommandLineRunner();
|
||||
};
|
||||
|
||||
#endif // __WinRemoteMessage_h__
|
|
@ -35,6 +35,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
|
|||
SOURCES += [
|
||||
'nsWinRemoteClient.cpp',
|
||||
'nsWinRemoteServer.cpp',
|
||||
'WinRemoteMessage.cpp',
|
||||
]
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
|
||||
SOURCES += [
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "nsWinRemoteClient.h"
|
||||
#include <windows.h>
|
||||
#include "RemoteUtils.h"
|
||||
#include "WinRemoteMessage.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
|
@ -27,25 +28,15 @@ nsresult nsWinRemoteClient::SendCommandLine(
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
WCHAR* cmd = ::GetCommandLineW();
|
||||
WCHAR cwd[MAX_PATH];
|
||||
_wgetcwd(cwd, MAX_PATH);
|
||||
WinRemoteMessageSender sender(::GetCommandLineW(), cwd);
|
||||
|
||||
// Construct a narrow UTF8 buffer <commandline>\0<workingdir>\0
|
||||
NS_ConvertUTF16toUTF8 utf8buffer(cmd);
|
||||
utf8buffer.Append('\0');
|
||||
WCHAR* cwdPtr = cwd;
|
||||
AppendUTF16toUTF8(MakeStringSpan(reinterpret_cast<char16_t*>(cwdPtr)),
|
||||
utf8buffer);
|
||||
utf8buffer.Append('\0');
|
||||
|
||||
// We used to set dwData to zero, when we didn't send the working dir.
|
||||
// Now we're using it as a version number.
|
||||
COPYDATASTRUCT cds = {1, utf8buffer.Length(), (void*)utf8buffer.get()};
|
||||
// Bring the already running Mozilla process to the foreground.
|
||||
// nsWindow will restore the window (if minimized) and raise it.
|
||||
::SetForegroundWindow(handle);
|
||||
::SendMessage(handle, WM_COPYDATA, 0, (LPARAM)&cds);
|
||||
::SendMessage(handle, WM_COPYDATA, 0,
|
||||
reinterpret_cast<LPARAM>(sender.CopyData()));
|
||||
|
||||
*aSucceeded = true;
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "nsICommandLine.h"
|
||||
#include "nsCommandLine.h"
|
||||
#include "nsIDocShell.h"
|
||||
#include "WinRemoteMessage.h"
|
||||
|
||||
HWND hwndForDOMWindow(mozIDOMWindowProxy* window) {
|
||||
if (!window) {
|
||||
|
@ -50,29 +51,9 @@ static nsresult GetMostRecentWindow(mozIDOMWindowProxy** aWindow) {
|
|||
|
||||
LRESULT CALLBACK WindowProc(HWND msgWindow, UINT msg, WPARAM wp, LPARAM lp) {
|
||||
if (msg == WM_COPYDATA) {
|
||||
// This is an incoming request.
|
||||
COPYDATASTRUCT* cds = (COPYDATASTRUCT*)lp;
|
||||
nsCOMPtr<nsIFile> workingDir;
|
||||
|
||||
if (1 >= cds->dwData) {
|
||||
char* wdpath = (char*)cds->lpData;
|
||||
// skip the command line, and get the working dir of the
|
||||
// other process, which is after the first null char
|
||||
while (*wdpath) ++wdpath;
|
||||
|
||||
++wdpath;
|
||||
|
||||
NS_NewLocalFile(NS_ConvertUTF8toUTF16(wdpath), false,
|
||||
getter_AddRefs(workingDir));
|
||||
}
|
||||
|
||||
mozilla::CommandLineParserWin<char> parser;
|
||||
parser.HandleCommandLine(reinterpret_cast<char*>(cds->lpData));
|
||||
|
||||
nsCOMPtr<nsICommandLineRunner> cmdLine(new nsCommandLine());
|
||||
if (NS_SUCCEEDED(cmdLine->Init(parser.Argc(), parser.Argv(), workingDir,
|
||||
nsICommandLine::STATE_REMOTE_AUTO))) {
|
||||
cmdLine->Run();
|
||||
WinRemoteMessageReceiver receiver;
|
||||
if (NS_SUCCEEDED(receiver.Parse(reinterpret_cast<COPYDATASTRUCT*>(lp)))) {
|
||||
receiver.CommandLineRunner()->Run();
|
||||
} else {
|
||||
NS_ERROR("Error initializing command line.");
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "mozilla/AssembleCmdLine.h"
|
||||
#include "mozilla/CmdLineAndEnvUtils.h"
|
||||
#include "mozilla/UniquePtrExtensions.h"
|
||||
#include "WinRemoteMessage.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
|
@ -21,6 +22,10 @@ struct TestCase {
|
|||
#define OMEGA_IN_UTF8 "\xe3\x82\xaa\xe3\x83\xa1\xe3\x82\xac"
|
||||
#define ALPHA_IN_UTF16 L"\u30A2\u30EB\u30D5\u30A1"
|
||||
#define OMEGA_IN_UTF16 L"\u30AA\u30E1\u30AC"
|
||||
#define UPPER_CYRILLIC_P_IN_UTF8 "\xd0\xa0"
|
||||
#define LOWER_CYRILLIC_P_IN_UTF8 "\xd1\x80"
|
||||
#define UPPER_CYRILLIC_P_IN_UTF16 L"\u0420"
|
||||
#define LOWER_CYRILLIC_P_IN_UTF16 L"\u0440"
|
||||
|
||||
TestCase<char> testCases[] = {
|
||||
// Copied from TestXREMakeCommandLineWin.ini
|
||||
|
@ -101,3 +106,68 @@ TEST(CommandLineParserWin, HandleCommandLine)
|
|||
EXPECT_EQ(testCase.mArgs[parser.Argc()], nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(WinRemoteMessage, SendReceive)
|
||||
{
|
||||
const char kCommandline[] =
|
||||
"dummy.exe /arg1 --arg2 \"3rd arg\" "
|
||||
"4th=\"" UPPER_CYRILLIC_P_IN_UTF8 " " LOWER_CYRILLIC_P_IN_UTF8 "\"";
|
||||
const wchar_t kCommandlineW[] =
|
||||
L"dummy.exe /arg1 --arg2 \"3rd arg\" "
|
||||
L"4th=\"" UPPER_CYRILLIC_P_IN_UTF16 L" " LOWER_CYRILLIC_P_IN_UTF16 L"\"";
|
||||
const wchar_t* kExpectedArgsW[] = {
|
||||
L"-arg1", L"-arg2", L"3rd arg",
|
||||
L"4th=" UPPER_CYRILLIC_P_IN_UTF16 L" " LOWER_CYRILLIC_P_IN_UTF16};
|
||||
|
||||
char workingDirA[MAX_PATH];
|
||||
wchar_t workingDirW[MAX_PATH];
|
||||
EXPECT_NE(getcwd(workingDirA, MAX_PATH), nullptr);
|
||||
EXPECT_NE(_wgetcwd(workingDirW, MAX_PATH), nullptr);
|
||||
|
||||
WinRemoteMessageSender v0(kCommandline);
|
||||
WinRemoteMessageSender v1(kCommandline, workingDirA);
|
||||
WinRemoteMessageSender v2(kCommandlineW, workingDirW);
|
||||
|
||||
WinRemoteMessageReceiver receiver;
|
||||
int32_t len;
|
||||
nsAutoString arg;
|
||||
nsCOMPtr<nsIFile> workingDir;
|
||||
|
||||
receiver.Parse(v0.CopyData());
|
||||
EXPECT_TRUE(NS_SUCCEEDED(receiver.CommandLineRunner()->GetLength(&len)));
|
||||
EXPECT_EQ(len, ArrayLength(kExpectedArgsW));
|
||||
for (int i = 0; i < ArrayLength(kExpectedArgsW); ++i) {
|
||||
EXPECT_TRUE(
|
||||
NS_SUCCEEDED(receiver.CommandLineRunner()->GetArgument(i, arg)));
|
||||
EXPECT_STREQ(arg.get(), kExpectedArgsW[i]);
|
||||
}
|
||||
EXPECT_EQ(receiver.CommandLineRunner()->GetWorkingDirectory(
|
||||
getter_AddRefs(workingDir)),
|
||||
NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
receiver.Parse(v1.CopyData());
|
||||
EXPECT_TRUE(NS_SUCCEEDED(receiver.CommandLineRunner()->GetLength(&len)));
|
||||
EXPECT_EQ(len, ArrayLength(kExpectedArgsW));
|
||||
for (int i = 0; i < ArrayLength(kExpectedArgsW); ++i) {
|
||||
EXPECT_TRUE(
|
||||
NS_SUCCEEDED(receiver.CommandLineRunner()->GetArgument(i, arg)));
|
||||
EXPECT_STREQ(arg.get(), kExpectedArgsW[i]);
|
||||
}
|
||||
EXPECT_TRUE(NS_SUCCEEDED(receiver.CommandLineRunner()->GetWorkingDirectory(
|
||||
getter_AddRefs(workingDir))));
|
||||
EXPECT_TRUE(NS_SUCCEEDED(workingDir->GetPath(arg)));
|
||||
EXPECT_STREQ(arg.get(), workingDirW);
|
||||
|
||||
receiver.Parse(v2.CopyData());
|
||||
EXPECT_TRUE(NS_SUCCEEDED(receiver.CommandLineRunner()->GetLength(&len)));
|
||||
EXPECT_EQ(len, ArrayLength(kExpectedArgsW));
|
||||
for (int i = 0; i < ArrayLength(kExpectedArgsW); ++i) {
|
||||
EXPECT_TRUE(
|
||||
NS_SUCCEEDED(receiver.CommandLineRunner()->GetArgument(i, arg)));
|
||||
EXPECT_STREQ(arg.get(), kExpectedArgsW[i]);
|
||||
}
|
||||
EXPECT_TRUE(NS_SUCCEEDED(receiver.CommandLineRunner()->GetWorkingDirectory(
|
||||
getter_AddRefs(workingDir))));
|
||||
EXPECT_TRUE(NS_SUCCEEDED(workingDir->GetPath(arg)));
|
||||
EXPECT_STREQ(arg.get(), workingDirW);
|
||||
}
|
||||
|
|
|
@ -10,6 +10,10 @@ UNIFIED_SOURCES = [
|
|||
'TestCompatVersionCompare.cpp',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/toolkit/components/remote',
|
||||
]
|
||||
|
||||
if CONFIG['OS_TARGET'] == 'WINNT':
|
||||
UNIFIED_SOURCES += [
|
||||
'TestAssembleCommandLineWin.cpp',
|
||||
|
|
Загрузка…
Ссылка в новой задаче