summaryrefslogtreecommitdiffstats
path: root/toolkit/components/remote/nsMacRemoteServer.mm
blob: f7c6ea234a3fc579360874d229259f193060908c (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
138
139
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:expandtab:shiftwidth=2:tabstop=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/. */

#import <Cocoa/Cocoa.h>
#import <CoreServices/CoreServices.h>

#include "MacAutoreleasePool.h"
#include "nsCOMPtr.h"
#include "nsIComponentManager.h"
#include "nsIServiceManager.h"
#include "nsIWindowMediator.h"
#include "nsIWidget.h"
#include "nsICommandLineRunner.h"
#include "nsICommandLine.h"
#include "nsCommandLine.h"
#include "nsIDocShell.h"
#include "nsMacRemoteServer.h"
#include "nsXPCOM.h"
#include "RemoteUtils.h"

CFDataRef messageServerCallback(CFMessagePortRef aLocal, int32_t aMsgid,
                                CFDataRef aData, void* aInfo) {
  // One of the clients submitted a structure.
  static_cast<nsMacRemoteServer*>(aInfo)->HandleCommandLine(aData);

  return NULL;
}

// aData contains serialized Dictionary, which in turn contains command line
// arguments
void nsMacRemoteServer::HandleCommandLine(CFDataRef aData) {
  mozilla::MacAutoreleasePool pool;

  if (aData) {
    NSDictionary* dict =
        [NSKeyedUnarchiver unarchiveObjectWithData:(NSData*)aData];
    if (dict && [dict isKindOfClass:[NSDictionary class]]) {
      NSArray* args = dict[@"args"];
      if (!args) {
        NS_ERROR("Wrong parameters passed to the Remote Server");
        return;
      }

      nsCOMPtr<nsICommandLineRunner> cmdLine(new nsCommandLine());

      // Converting Objective-C array into a C array,
      // which nsICommandLineRunner understands.
      int argc = [args count];
      const char** argv = new const char*[argc];
      for (int i = 0; i < argc; i++) {
        const char* arg = [[args objectAtIndex:i] UTF8String];
        argv[i] = arg;
      }

      nsresult rv =
          cmdLine->Init(argc, argv, nullptr, nsICommandLine::STATE_REMOTE_AUTO);

      // Cleaning up C array.
      delete[] argv;

      if (NS_FAILED(rv)) {
        NS_ERROR("Error initializing command line.");
        return;
      }

      // Processing the command line, passed from a remote instance
      // in the current instance.
      cmdLine->Run();

      // And bring the app's window to front.
      [[NSRunningApplication currentApplication]
          activateWithOptions:NSApplicationActivateIgnoringOtherApps];
    }
  }
}

nsresult nsMacRemoteServer::Startup(const char* aAppName,
                                    const char* aProfileName) {
  // This is the first instance ever.
  // Let's register a notification listener here,
  // In case future instances would want to notify us about command line
  // arguments passed to them. Note, that if mozilla process is restarting, we
  // still need to register for notifications.

  mozilla::MacAutoreleasePool pool;

  nsString className;
  BuildClassName(aAppName, aProfileName, className);

  NSString* serverNameString = [NSString
      stringWithCharacters:reinterpret_cast<const unichar*>(className.get())
                    length:className.Length()];

  CFMessagePortContext context;
  context.copyDescription = NULL;
  context.info = this;
  context.release = NULL;
  context.retain = NULL;
  context.version = NULL;
  mMessageServer =
      CFMessagePortCreateLocal(NULL, (CFStringRef)serverNameString,
                               messageServerCallback, &context, NULL);
  if (!mMessageServer) {
    return NS_ERROR_FAILURE;
  }
  mRunLoopSource = CFMessagePortCreateRunLoopSource(NULL, mMessageServer, 0);
  if (!mRunLoopSource) {
    CFRelease(mMessageServer);
    mMessageServer = NULL;
    return NS_ERROR_FAILURE;
  }
  CFRunLoopRef runLoop = CFRunLoopGetMain();
  CFRunLoopAddSource(runLoop, mRunLoopSource, kCFRunLoopDefaultMode);

  return NS_OK;
}

void nsMacRemoteServer::Shutdown() {
  // 1) Invalidate server connection
  if (mMessageServer) {
    CFMessagePortInvalidate(mMessageServer);
  }

  // 2) Release run loop source
  if (mRunLoopSource) {
    CFRelease(mRunLoopSource);
    mRunLoopSource = NULL;
  }

  // 3) Release server connection
  if (mMessageServer) {
    CFRelease(mMessageServer);
    mMessageServer = NULL;
  }
}