bool ElectronBundleMover::Move()

in shell/browser/ui/cocoa/electron_bundle_mover.mm [64:179]


bool ElectronBundleMover::Move(gin_helper::ErrorThrower thrower,
                               gin::Arguments* args) {
  // Path of the current bundle
  NSString* bundlePath = [[NSBundle mainBundle] bundlePath];

  // Skip if the application is already in the Applications folder
  if (IsInApplicationsFolder(bundlePath))
    return true;

  NSFileManager* fileManager = [NSFileManager defaultManager];

  NSString* diskImageDevice = ContainingDiskImageDevice(bundlePath);

  NSString* applicationsDirectory = [[NSSearchPathForDirectoriesInDomains(
      NSApplicationDirectory, NSLocalDomainMask, true) lastObject]
      stringByResolvingSymlinksInPath];
  NSString* bundleName = [bundlePath lastPathComponent];
  NSString* destinationPath =
      [applicationsDirectory stringByAppendingPathComponent:bundleName];

  // Check if we can write to the applications directory
  // and then make sure that if the app already exists we can overwrite it
  bool needAuthorization =
      ![fileManager isWritableFileAtPath:applicationsDirectory] ||
      ([fileManager fileExistsAtPath:destinationPath] &&
       ![fileManager isWritableFileAtPath:destinationPath]);

  // Activate app -- work-around for focus issues related to "scary file from
  // internet" OS dialog.
  if (![NSApp isActive]) {
    [NSApp activateIgnoringOtherApps:true];
  }

  // Move to applications folder
  if (needAuthorization) {
    bool authorizationCanceled;

    if (!AuthorizedInstall(bundlePath, destinationPath,
                           &authorizationCanceled)) {
      if (authorizationCanceled) {
        // User rejected the authorization request
        thrower.ThrowError("User rejected the authorization request");
        return false;
      } else {
        thrower.ThrowError(
            "Failed to copy to applications directory even with authorization");
        return false;
      }
    }
  } else {
    // If a copy already exists in the Applications folder, put it in the Trash
    if ([fileManager fileExistsAtPath:destinationPath]) {
      // But first, make sure that it's not running
      if (IsApplicationAtPathRunning(destinationPath)) {
        // Check for callback handler and get user choice for open/quit
        if (!ShouldContinueMove(
                thrower, BundlerMoverConflictType::kExistsAndRunning, args))
          return false;

        // Unless explicitly denied, give running app focus and terminate self
        [[NSTask
            launchedTaskWithLaunchPath:@"/usr/bin/open"
                             arguments:[NSArray
                                           arrayWithObject:destinationPath]]
            waitUntilExit];
        electron::Browser::Get()->Quit();
        return true;
      } else {
        // Check callback handler and get user choice for app trashing
        if (!ShouldContinueMove(thrower, BundlerMoverConflictType::kExists,
                                args))
          return false;

        // Unless explicitly denied, attempt to trash old app
        if (!Trash([applicationsDirectory
                stringByAppendingPathComponent:bundleName])) {
          thrower.ThrowError("Failed to delete existing application");
          return false;
        }
      }
    }

    if (!CopyBundle(bundlePath, destinationPath)) {
      thrower.ThrowError(
          "Failed to copy current bundle to the applications folder");
      return false;
    }
  }

  // Trash the original app. It's okay if this fails.
  // NOTE: This final delete does not work if the source bundle is in a network
  // mounted volume.
  //       Calling rm or file manager's delete method doesn't work either. It's
  //       unlikely to happen but it'd be great if someone could fix this.
  if (diskImageDevice == nil && !DeleteOrTrash(bundlePath)) {
    // Could not delete original but we just don't care
  }

  // Relaunch.
  Relaunch(destinationPath);

  // Launched from within a disk image? -- unmount (if no files are open after 5
  // seconds, otherwise leave it mounted).
  if (diskImageDevice) {
    NSString* script = [NSString
        stringWithFormat:@"(/bin/sleep 5 && /usr/bin/hdiutil detach %@) &",
                         ShellQuotedString(diskImageDevice)];
    [NSTask launchedTaskWithLaunchPath:@"/bin/sh"
                             arguments:[NSArray arrayWithObjects:@"-c", script,
                                                                 nil]];
  }

  electron::Browser::Get()->Quit();

  return true;
}