chromium/chrome/browser/shortcuts/chrome_webloc_file.mm

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/shortcuts/chrome_webloc_file.h"

#import <Foundation/Foundation.h>

#include <utility>

#include "base/apple/foundation_util.h"
#include "base/files/block_tests_writing_to_special_dirs.h"
#include "base/logging.h"
#include "base/strings/sys_string_conversions.h"
#include "base/threading/scoped_blocking_call.h"

namespace shortcuts {

namespace {

NSString* const kUrlKey = @"URL";
NSString* const kProfileKey = @"CrProfile";

}  // namespace

ChromeWeblocFile::ChromeWeblocFile(GURL target_url,
                                   base::SafeBaseName profile_path_name)
    : target_url_(std::move(target_url)),
      profile_path_name_(std::move(profile_path_name)) {}

ChromeWeblocFile::~ChromeWeblocFile() = default;

// static
std::optional<ChromeWeblocFile> ChromeWeblocFile::LoadFromFile(
    const base::FilePath& file_path) {
  NSDictionary* contents;
  NSError* error;
  {
    base::ScopedBlockingCall scoped_blocking_call(
        FROM_HERE, base::BlockingType::MAY_BLOCK);
    contents = [NSDictionary
        dictionaryWithContentsOfURL:base::apple::FilePathToNSURL(file_path)
                              error:&error];
  }
  if (!contents) {
    LOG(ERROR) << "Failed to read shortcut file from " << file_path << ": "
               << error;
    return std::nullopt;
  }
  GURL target_url(base::SysNSStringToUTF8(contents[kUrlKey]));
  std::optional<base::SafeBaseName> profile_path_name =
      base::SafeBaseName::Create(
          base::apple::NSStringToFilePath(contents[kProfileKey]));
  if (!target_url.is_valid() || !profile_path_name.has_value() ||
      profile_path_name->path().empty()) {
    LOG(ERROR) << "Failed to parse URL or profile from " << file_path << " "
               << contents;
    return std::nullopt;
  }
  return ChromeWeblocFile(std::move(target_url), *std::move(profile_path_name));
}

bool ChromeWeblocFile::SaveToFile(const base::FilePath& file_path) {
  if (!base::BlockTestsWritingToSpecialDirs::CanWriteToPath(file_path)) {
    return false;
  }

  NSDictionary* contents = @{
    kUrlKey : base::SysUTF8ToNSString(target_url_.spec()),
    kProfileKey : base::apple::FilePathToNSString(profile_path_name_.path()),
  };
  {
    base::ScopedBlockingCall scoped_blocking_call(
        FROM_HERE, base::BlockingType::MAY_BLOCK);
    NSError* error = nil;
    if (![contents writeToURL:base::apple::FilePathToNSURL(file_path)
                        error:&error]) {
      LOG(ERROR) << "Failed to create shortcut at " << file_path << ": "
                 << error;
      return false;
    }
  }
  return true;
}

}  // namespace shortcuts