<commit_before>/* Copyright 2014-present Facebook, Inc.
 * Licensed under the Apache License, Version 2.0 */

#include "watchman.h"
#include <folly/Synchronized.h>
#include "FileDescriptor.h"
#include "InMemoryView.h"

#include <algorithm>
#include <condition_variable>
#include <iterator>
#include <list>
#include <mutex>
#include <tuple>

using watchman::FileDescriptor;
using namespace watchman;

#ifdef _WIN32

#define NETWORK_BUF_SIZE (64 * 1024)

namespace {

struct Item {
  w_string path;
  int flags;

  Item(w_string&& path, int flags) : path(std::move(path)), flags(flags) {}
};

} // namespace

struct WinWatcher : public Watcher {
  HANDLE ping{INVALID_HANDLE_VALUE}, olapEvent{INVALID_HANDLE_VALUE};
  FileDescriptor dir_handle;

  std::condition_variable cond;
  folly::Synchronized<std::list<Item>, std::mutex> changedItems;

  explicit WinWatcher(watchman_root* root);
  ~WinWatcher();

  std::unique_ptr<watchman_dir_handle> startWatchDir(
      const std::shared_ptr<watchman_root>& root,
      struct watchman_dir* dir,
      const char* path) override;

  Watcher::ConsumeNotifyRet consumeNotify(
      const std::shared_ptr<watchman_root>& root,
      PendingChanges& coll) override;

  bool waitNotify(int timeoutms) override;
  bool start(const std::shared_ptr<watchman_root>& root) override;
  void signalThreads() override;
  void readChangesThread(const std::shared_ptr<watchman_root>& root);
};

WinWatcher::WinWatcher(watchman_root* root)
    : Watcher("win32", WATCHER_HAS_PER_FILE_NOTIFICATIONS) {
  int err;

  auto wpath = root->root_path.piece().asWideUNC();

  // Create an overlapped handle so that we can avoid blocking forever
  // in ReadDirectoryChangesW
  dir_handle = FileDescriptor(
      intptr_t(CreateFileW(
          wpath.c_str(),
          GENERIC_READ,
          FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
          nullptr,
          OPEN_EXISTING,
          FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
          nullptr)),
      FileDescriptor::FDType::Generic);

  if (!dir_handle) {
    throw std::runtime_error(
        std::string("failed to open dir ") + root->root_path.c_str() + ": " +
        win32_strerror(GetLastError()));
  }

  ping = CreateEvent(nullptr, TRUE, FALSE, nullptr);
  if (!ping) {
    throw std::runtime_error(
        std::string("failed to create event: ") +
        win32_strerror(GetLastError()));
  }
  olapEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
  if (!olapEvent) {
    throw std::runtime_error(
        std::string("failed to create event: ") +
        win32_strerror(GetLastError()));
  }
}

WinWatcher::~WinWatcher() {
  if (ping != INVALID_HANDLE_VALUE) {
    CloseHandle(ping);
  }
  if (olapEvent != INVALID_HANDLE_VALUE) {
    CloseHandle(olapEvent);
  }
}

void WinWatcher::signalThreads() {
  SetEvent(ping);
}

void WinWatcher::readChangesThread(const std::shared_ptr<watchman_root>& root) {
  std::vector<uint8_t> buf;
  DWORD err, filter;
  auto olap = OVERLAPPED();
  BOOL initiate_read = true;
  HANDLE handles[2] = {olapEvent, ping};
  DWORD bytes;

  w_set_thread_name("readchange ", root->root_path);
  watchman::log(watchman::DBG, "initializing\n");

  // Artificial extra latency to impose around processing changes.
  // This is needed to avoid trying to access files and dirs too
  // soon after a change is noticed, as this can cause recursive
  // deletes to fail.
  auto extraLatency = root->config.getInt("win32_batch_latency_ms", 30);

  DWORD size = root->config.getInt("win32_rdcw_buf_size", 16384);

  // Block until winmatch_root_st is waiting for our initialization
  {
    auto wlock = changedItems.lock();

    filter = FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME |
        FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE |
        FILE_NOTIFY_CHANGE_LAST_WRITE;

    olap.hEvent = olapEvent;

    buf.resize(size);

    if (!ReadDirectoryChangesW(
            (HANDLE)dir_handle.handle(),
            &buf[0],
            size,
            TRUE,
            filter,
            nullptr,
            &olap,
            nullptr)) {
      err = GetLastError();
      logf(
          ERR,
          "ReadDirectoryChangesW: failed, cancel watch. {}\n",
          win32_strerror(err));
      root->cancel();
      return;
    }
    // Signal that we are done with init.  We MUST do this AFTER our first
    // successful ReadDirectoryChangesW, otherwise there is a race condition
    // where we'll miss observing the cookie for a query that comes in
    // after we've crawled but before the watch is established.
    logf(DBG, "ReadDirectoryChangesW signalling as init done\n");
    cond.notify_one();
  }
  initiate_read = false;

  std::list<Item> items;

  // The mutex must not be held when we enter the loop
  while (!root->inner.cancelled) {
    if (initiate_read) {
      if (!ReadDirectoryChangesW(
              (HANDLE)dir_handle.handle(),
              &buf[0],
              size,
              TRUE,
              filter,
              nullptr,
              &olap,
              nullptr)) {
        err = GetLastError();
        logf(
            ERR,
            "ReadDirectoryChangesW: failed, cancel watch. {}\n",
            win32_strerror(err));
        root->cancel();
        break;
      } else {
        initiate_read = false;
      }
    }

    watchman::log(watchman::DBG, "waiting for change notifications\n");
    DWORD status = WaitForMultipleObjects(
        2,
        handles,
        FALSE,
        // We use a 10 second timeout by default until we start accumulating a
        // batch.  Once we have a batch we prefer to add more to it than notify
        // immediately, so we introduce a 30ms latency.  Without this artificial
        // latency we'll wake up and start trying to look at a directory that
        // may be in the process of being recursively deleted and that act can
        // block the recursive delete.
        items.empty() ? 10000 : extraLatency);
    watchman::log(watchman::DBG, "wait returned with status ", status, "\n");

    if (status == WAIT_OBJECT_0) {
      bytes = 0;
      if (!GetOverlappedResult(
              (HANDLE)dir_handle.handle(), &olap, &bytes, FALSE)) {
        err = GetLastError();
        logf(
            ERR,
            "overlapped ReadDirectoryChangesW({}): {:x} {}\n",
            root->root_path,
            err,
            win32_strerror(err));

        if (err == ERROR_INVALID_PARAMETER && size > NETWORK_BUF_SIZE) {
          // May be a network buffer related size issue; the docs say that
          // we can hit this when watching a UNC path. Let's downsize and
          // retry the read just one time
          logf(
              ERR,
              "retrying watch for possible network location {} "
              "with smaller buffer\n",
              root->root_path);
          size = NETWORK_BUF_SIZE;
          initiate_read = true;
          continue;
        }

        if (err == ERROR_NOTIFY_ENUM_DIR) {
          root->scheduleRecrawl("ERROR_NOTIFY_ENUM_DIR");
        } else {
          logf(ERR, "Cancelling watch for {}\n", root->root_path);
          root->cancel();
          break;
        }
      } else {
        PFILE_NOTIFY_INFORMATION notify = (PFILE_NOTIFY_INFORMATION)buf.data();

        while (true) {
          DWORD n_chars;

          // FileNameLength is in BYTES, but FileName is WCHAR
          n_chars = notify->FileNameLength / sizeof(notify->FileName[0]);
          w_string name(notify->FileName, n_chars);

          auto full = w_string::pathCat({root->root_path, name});

          if (!root->ignore.isIgnored(full.data(), full.size())) {
            // If we have a delete or rename-away it may be part of
            // a recursive tree remove or rename.  In that situation
            // the notifications that we'll receive from the OS will
            // be from the leaves and bubble up to the root of the
            // delete/rename.  We want to flag those paths for recursive
            // analysis so that we can prune children from the trie
            // that is built when we pass this to the pending list
            // later.  We don't do that here in this thread because
            // we're trying to minimize latency in this context.
            items.emplace_back(
                std::move(full),
                (notify->Action &
                 (FILE_ACTION_REMOVED | FILE_ACTION_RENAMED_OLD_NAME)) != 0
                    ? W_PENDING_RECURSIVE
                    : 0);
          }

          // Advance to next item
          if (notify->NextEntryOffset == 0) {
            break;
          }
          notify = (PFILE_NOTIFY_INFORMATION)(
              notify->NextEntryOffset + (char*)notify);
        }

        ResetEvent(olapEvent);
        initiate_read = true;
      }
    } else if (status == WAIT_OBJECT_0 + 1) {
      logf(ERR, "signalled\n");
      break;
    } else if (status == WAIT_TIMEOUT) {
      if (!items.empty()) {
        watchman::log(
            watchman::DBG,
            "timed out waiting for changes, and we have ",
            items.size(),
            " items; move and notify\n");
        auto wlock = changedItems.lock();
        wlock->splice(wlock->end(), items);
        cond.notify_one();
      }
    } else {
      logf(ERR, "impossible wait status={}\n", status);
      break;
    }
  }

  logf(DBG, "done\n");
}

bool WinWatcher::start(const std::shared_ptr<watchman_root>& root) {
  int err;

  // Spin up the changes reading thread; it owns a ref on the root

  try {
    // Acquire the mutex so thread initialization waits until we release it
    auto wlock = changedItems.lock();

    watchman::log(watchman::DBG, "starting readChangesThread\n");
    auto self = std::dynamic_pointer_cast<WinWatcher>(shared_from_this());
    std::thread thread([self, root]() noexcept {
      try {
        self->readChangesThread(root);
      } catch (const std::exception& e) {
        watchman::log(watchman::ERR, "uncaught exception: ", e.what());
        root->cancel();
      }

      // Ensure that we signal the condition variable before we
      // finish this thread.  That ensures that don't get stuck
      // waiting in WinWatcher::start if something unexpected happens.
      auto wlock = self->changedItems.lock();
      self->cond.notify_one();
    });
    // We have to detach because the readChangesThread may wind up
    // being the last thread to reference the watcher state and
    // cannot join itself.
    thread.detach();

    // Allow thread init to proceed; wait for its signal
    if (cond.wait_for(wlock.getUniqueLock(), std::chrono::seconds(10)) ==
        std::cv_status::timeout) {
      watchman::log(
          watchman::ERR, "timedout waiting for readChangesThread to start\n");
      root->cancel();
      return false;
    }

    if (root->failure_reason) {
      logf(
          ERR,
          "failed to start readchanges thread: {}\n",
          root->failure_reason);
      return false;
    }
    return true;
  } catch (const std::exception& e) {
    logf(ERR, "failed to start readchanges thread: {}\n", e.what());
    return false;
  }
}

std::unique_ptr<watchman_dir_handle> WinWatcher::startWatchDir(
    const std::shared_ptr<watchman_root>& root,
    struct watchman_dir* dir,
    const char* path) {
  return w_dir_open(path);
}

Watcher::ConsumeNotifyRet WinWatcher::consumeNotify(
    const std::shared_ptr<watchman_root>& root,
    PendingChanges& coll) {
  std::list<Item> items;

  {
    auto wlock = changedItems.lock();
    std::swap(items, *wlock);
  }

  auto now = std::chrono::system_clock::now();

  for (auto& item : items) {
    watchman::log(
        watchman::DBG,
        "readchanges: add pending ",
        item.path,
        " ",
        item.flags,
        "\n");
    coll.add(item.path, now, W_PENDING_VIA_NOTIFY | item.flags);
  }

  // The readChangesThread cancels itself.
  return {!items.empty(), false};
}

bool WinWatcher::waitNotify(int timeoutms) {
  auto wlock = changedItems.lock();
  cond.wait_for(wlock.getUniqueLock(), std::chrono::milliseconds(timeoutms));
  return !wlock->empty();
}

static RegisterWatcher<WinWatcher> reg("win32");

#endif // _WIN32

/* vim:ts=2:sw=2:et:
 */
<commit_msg>Apply clang-format<commit_after>/* Copyright 2014-present Facebook, Inc.
 * Licensed under the Apache License, Version 2.0 */

#include "watchman.h"
#include <folly/Synchronized.h>
#include "FileDescriptor.h"
#include "InMemoryView.h"

#include <algorithm>
#include <condition_variable>
#include <iterator>
#include <list>
#include <mutex>
#include <tuple>

using watchman::FileDescriptor;
using namespace watchman;

#ifdef _WIN32

#define NETWORK_BUF_SIZE (64 * 1024)

namespace {

struct Item {
  w_string path;
  int flags;

  Item(w_string&& path, int flags) : path(std::move(path)), flags(flags) {}
};

} // namespace

struct WinWatcher : public Watcher {
  HANDLE ping{INVALID_HANDLE_VALUE}, olapEvent{INVALID_HANDLE_VALUE};
  FileDescriptor dir_handle;

  std::condition_variable cond;
  folly::Synchronized<std::list<Item>, std::mutex> changedItems;

  explicit WinWatcher(watchman_root* root);
  ~WinWatcher();

  std::unique_ptr<watchman_dir_handle> startWatchDir(
      const std::shared_ptr<watchman_root>& root,
      struct watchman_dir* dir,
      const char* path) override;

  Watcher::ConsumeNotifyRet consumeNotify(
      const std::shared_ptr<watchman_root>& root,
      PendingChanges& coll) override;

  bool waitNotify(int timeoutms) override;
  bool start(const std::shared_ptr<watchman_root>& root) override;
  void signalThreads() override;
  void readChangesThread(const std::shared_ptr<watchman_root>& root);
};

WinWatcher::WinWatcher(watchman_root* root)
    : Watcher("win32", WATCHER_HAS_PER_FILE_NOTIFICATIONS) {
  int err;

  auto wpath = root->root_path.piece().asWideUNC();

  // Create an overlapped handle so that we can avoid blocking forever
  // in ReadDirectoryChangesW
  dir_handle = FileDescriptor(
      intptr_t(CreateFileW(
          wpath.c_str(),
          GENERIC_READ,
          FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
          nullptr,
          OPEN_EXISTING,
          FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
          nullptr)),
      FileDescriptor::FDType::Generic);

  if (!dir_handle) {
    throw std::runtime_error(
        std::string("failed to open dir ") + root->root_path.c_str() + ": " +
        win32_strerror(GetLastError()));
  }

  ping = CreateEvent(nullptr, TRUE, FALSE, nullptr);
  if (!ping) {
    throw std::runtime_error(
        std::string("failed to create event: ") +
        win32_strerror(GetLastError()));
  }
  olapEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
  if (!olapEvent) {
    throw std::runtime_error(
        std::string("failed to create event: ") +
        win32_strerror(GetLastError()));
  }
}

WinWatcher::~WinWatcher() {
  if (ping != INVALID_HANDLE_VALUE) {
    CloseHandle(ping);
  }
  if (olapEvent != INVALID_HANDLE_VALUE) {
    CloseHandle(olapEvent);
  }
}

void WinWatcher::signalThreads() {
  SetEvent(ping);
}

void WinWatcher::readChangesThread(const std::shared_ptr<watchman_root>& root) {
  std::vector<uint8_t> buf;
  DWORD err, filter;
  auto olap = OVERLAPPED();
  BOOL initiate_read = true;
  HANDLE handles[2] = {olapEvent, ping};
  DWORD bytes;

  w_set_thread_name("readchange ", root->root_path);
  watchman::log(watchman::DBG, "initializing\n");

  // Artificial extra latency to impose around processing changes.
  // This is needed to avoid trying to access files and dirs too
  // soon after a change is noticed, as this can cause recursive
  // deletes to fail.
  auto extraLatency = root->config.getInt("win32_batch_latency_ms", 30);

  DWORD size = root->config.getInt("win32_rdcw_buf_size", 16384);

  // Block until winmatch_root_st is waiting for our initialization
  {
    auto wlock = changedItems.lock();

    filter = FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME |
        FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE |
        FILE_NOTIFY_CHANGE_LAST_WRITE;

    olap.hEvent = olapEvent;

    buf.resize(size);

    if (!ReadDirectoryChangesW(
            (HANDLE)dir_handle.handle(),
            &buf[0],
            size,
            TRUE,
            filter,
            nullptr,
            &olap,
            nullptr)) {
      err = GetLastError();
      logf(
          ERR,
          "ReadDirectoryChangesW: failed, cancel watch. {}\n",
          win32_strerror(err));
      root->cancel();
      return;
    }
    // Signal that we are done with init.  We MUST do this AFTER our first
    // successful ReadDirectoryChangesW, otherwise there is a race condition
    // where we'll miss observing the cookie for a query that comes in
    // after we've crawled but before the watch is established.
    logf(DBG, "ReadDirectoryChangesW signalling as init done\n");
    cond.notify_one();
  }
  initiate_read = false;

  std::list<Item> items;

  // The mutex must not be held when we enter the loop
  while (!root->inner.cancelled) {
    if (initiate_read) {
      if (!ReadDirectoryChangesW(
              (HANDLE)dir_handle.handle(),
              &buf[0],
              size,
              TRUE,
              filter,
              nullptr,
              &olap,
              nullptr)) {
        err = GetLastError();
        logf(
            ERR,
            "ReadDirectoryChangesW: failed, cancel watch. {}\n",
            win32_strerror(err));
        root->cancel();
        break;
      } else {
        initiate_read = false;
      }
    }

    watchman::log(watchman::DBG, "waiting for change notifications\n");
    DWORD status = WaitForMultipleObjects(
        2,
        handles,
        FALSE,
        // We use a 10 second timeout by default until we start accumulating a
        // batch.  Once we have a batch we prefer to add more to it than notify
        // immediately, so we introduce a 30ms latency.  Without this artificial
        // latency we'll wake up and start trying to look at a directory that
        // may be in the process of being recursively deleted and that act can
        // block the recursive delete.
        items.empty() ? 10000 : extraLatency);
    watchman::log(watchman::DBG, "wait returned with status ", status, "\n");

    if (status == WAIT_OBJECT_0) {
      bytes = 0;
      if (!GetOverlappedResult(
              (HANDLE)dir_handle.handle(), &olap, &bytes, FALSE)) {
        err = GetLastError();
        logf(
            ERR,
            "overlapped ReadDirectoryChangesW({}): {:x} {}\n",
            root->root_path,
            err,
            win32_strerror(err));

        if (err == ERROR_INVALID_PARAMETER && size > NETWORK_BUF_SIZE) {
          // May be a network buffer related size issue; the docs say that
          // we can hit this when watching a UNC path. Let's downsize and
          // retry the read just one time
          logf(
              ERR,
              "retrying watch for possible network location {} "
              "with smaller buffer\n",
              root->root_path);
          size = NETWORK_BUF_SIZE;
          initiate_read = true;
          continue;
        }

        if (err == ERROR_NOTIFY_ENUM_DIR) {
          root->scheduleRecrawl("ERROR_NOTIFY_ENUM_DIR");
        } else {
          logf(ERR, "Cancelling watch for {}\n", root->root_path);
          root->cancel();
          break;
        }
      } else {
        PFILE_NOTIFY_INFORMATION notify = (PFILE_NOTIFY_INFORMATION)buf.data();

        while (true) {
          DWORD n_chars;

          // FileNameLength is in BYTES, but FileName is WCHAR
          n_chars = notify->FileNameLength / sizeof(notify->FileName[0]);
          w_string name(notify->FileName, n_chars);

          auto full = w_string::pathCat({root->root_path, name});

          if (!root->ignore.isIgnored(full.data(), full.size())) {
            // If we have a delete or rename-away it may be part of
            // a recursive tree remove or rename.  In that situation
            // the notifications that we'll receive from the OS will
            // be from the leaves and bubble up to the root of the
            // delete/rename.  We want to flag those paths for recursive
            // analysis so that we can prune children from the trie
            // that is built when we pass this to the pending list
            // later.  We don't do that here in this thread because
            // we're trying to minimize latency in this context.
            items.emplace_back(
                std::move(full),
                (notify->Action &
                 (FILE_ACTION_REMOVED | FILE_ACTION_RENAMED_OLD_NAME)) != 0
                    ? W_PENDING_RECURSIVE
                    : 0);
          }

          // Advance to next item
          if (notify->NextEntryOffset == 0) {
            break;
          }
          notify =
              (PFILE_NOTIFY_INFORMATION)(notify->NextEntryOffset + (char*)notify);
        }

        ResetEvent(olapEvent);
        initiate_read = true;
      }
    } else if (status == WAIT_OBJECT_0 + 1) {
      logf(ERR, "signalled\n");
      break;
    } else if (status == WAIT_TIMEOUT) {
      if (!items.empty()) {
        watchman::log(
            watchman::DBG,
            "timed out waiting for changes, and we have ",
            items.size(),
            " items; move and notify\n");
        auto wlock = changedItems.lock();
        wlock->splice(wlock->end(), items);
        cond.notify_one();
      }
    } else {
      logf(ERR, "impossible wait status={}\n", status);
      break;
    }
  }

  logf(DBG, "done\n");
}

bool WinWatcher::start(const std::shared_ptr<watchman_root>& root) {
  int err;

  // Spin up the changes reading thread; it owns a ref on the root

  try {
    // Acquire the mutex so thread initialization waits until we release it
    auto wlock = changedItems.lock();

    watchman::log(watchman::DBG, "starting readChangesThread\n");
    auto self = std::dynamic_pointer_cast<WinWatcher>(shared_from_this());
    std::thread thread([self, root]() noexcept {
      try {
        self->readChangesThread(root);
      } catch (const std::exception& e) {
        watchman::log(watchman::ERR, "uncaught exception: ", e.what());
        root->cancel();
      }

      // Ensure that we signal the condition variable before we
      // finish this thread.  That ensures that don't get stuck
      // waiting in WinWatcher::start if something unexpected happens.
      auto wlock = self->changedItems.lock();
      self->cond.notify_one();
    });
    // We have to detach because the readChangesThread may wind up
    // being the last thread to reference the watcher state and
    // cannot join itself.
    thread.detach();

    // Allow thread init to proceed; wait for its signal
    if (cond.wait_for(wlock.getUniqueLock(), std::chrono::seconds(10)) ==
        std::cv_status::timeout) {
      watchman::log(
          watchman::ERR, "timedout waiting for readChangesThread to start\n");
      root->cancel();
      return false;
    }

    if (root->failure_reason) {
      logf(
          ERR,
          "failed to start readchanges thread: {}\n",
          root->failure_reason);
      return false;
    }
    return true;
  } catch (const std::exception& e) {
    logf(ERR, "failed to start readchanges thread: {}\n", e.what());
    return false;
  }
}

std::unique_ptr<watchman_dir_handle> WinWatcher::startWatchDir(
    const std::shared_ptr<watchman_root>& root,
    struct watchman_dir* dir,
    const char* path) {
  return w_dir_open(path);
}

Watcher::ConsumeNotifyRet WinWatcher::consumeNotify(
    const std::shared_ptr<watchman_root>& root,
    PendingChanges& coll) {
  std::list<Item> items;

  {
    auto wlock = changedItems.lock();
    std::swap(items, *wlock);
  }

  auto now = std::chrono::system_clock::now();

  for (auto& item : items) {
    watchman::log(
        watchman::DBG,
        "readchanges: add pending ",
        item.path,
        " ",
        item.flags,
        "\n");
    coll.add(item.path, now, W_PENDING_VIA_NOTIFY | item.flags);
  }

  // The readChangesThread cancels itself.
  return {!items.empty(), false};
}

bool WinWatcher::waitNotify(int timeoutms) {
  auto wlock = changedItems.lock();
  cond.wait_for(wlock.getUniqueLock(), std::chrono::milliseconds(timeoutms));
  return !wlock->empty();
}

static RegisterWatcher<WinWatcher> reg("win32");

#endif // _WIN32

/* vim:ts=2:sw=2:et:
 */
<|endoftext|>


<commit_before>//
//  libavg - Media Playback Engine.
//  Copyright (C) 2003-2011 Ulrich von Zadow
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//  Current versions can be found at www.libavg.de
//

#include "DivNode.h"
#include "Player.h"
#include "NodeDefinition.h"
#include "Canvas.h"

#include "../base/Point.h"
#include "../base/Exception.h"
#include "../base/Logger.h"
#include "../base/StringHelper.h"
#include "../base/FileHelper.h"
#include "../base/MathHelper.h"
#include "../base/ObjectCounter.h"

#include <iostream>
#include <sstream>
#include <limits>

using namespace std;
using namespace boost;

#define DEFAULT_SIZE 100000

namespace avg {

NodeDefinition DivNode::createDefinition()
{
    string sChildArray[] = {"image", "div", "canvas", "words", "video", "camera",
            "panoimage", "sound", "line", "rect", "curve", "polyline", "polygon",
            "circle", "mesh"};
    vector<string> sChildren = vectorFromCArray(
            sizeof(sChildArray) / sizeof(*sChildArray), sChildArray);
    return NodeDefinition("div", Node::buildNode<DivNode>)
        .extendDefinition(AreaNode::createDefinition())
        .addChildren(sChildren)
        .addArg(Arg<bool>("crop", false, false, offsetof(DivNode, m_bCrop)))
        .addArg(Arg<string>("elementoutlinecolor", "", false,
                offsetof(DivNode, m_sElementOutlineColor)))
        .addArg(Arg<UTF8String>("mediadir", "", false, offsetof(DivNode, m_sMediaDir)));
}

DivNode::DivNode(const ArgList& args)
{
    args.setMembers(this);
    setElementOutlineColor(m_sElementOutlineColor);
    ObjectCounter::get()->incRef(&typeid(*this));
}

DivNode::~DivNode()
{
    ObjectCounter::get()->decRef(&typeid(*this));
}

void DivNode::connectDisplay()
{
    AreaNode::connectDisplay();
    for (unsigned i = 0; i < getNumChildren(); ++i) {
        getChild(i)->connectDisplay();
    }
    m_pClipVertexes = VertexArrayPtr(new VertexArray());
}

void DivNode::connect(CanvasPtr pCanvas)
{
    AreaNode::connect(pCanvas);
    for (unsigned i = 0; i < getNumChildren(); ++i) {
        getChild(i)->connect(pCanvas);
    }
}

void DivNode::disconnect(bool bKill)
{
    for (unsigned i = 0; i < getNumChildren(); ++i) {
        getChild(i)->disconnect(bKill);
    }
    if (m_pClipVertexes) {
        m_pClipVertexes = VertexArrayPtr();
    }
    AreaNode::disconnect(bKill);
}

DPoint DivNode::getPivot() const
{
    DPoint pivot = AreaNode::getPivot();
    if (pivot == DPoint(DEFAULT_SIZE / 2, DEFAULT_SIZE / 2)) {
        return DPoint(0, 0);
    }
    return pivot;
}

unsigned DivNode::getNumChildren()
{
    return m_Children.size();
}

const NodePtr& DivNode::getChild(unsigned i)
{
    if (i >= m_Children.size()) {
        stringstream s;
        s << "Index " << i << " is out of range in Node::getChild()";
        throw(Exception(AVG_ERR_OUT_OF_RANGE, s.str()));
    }
    return m_Children[i];
}

void DivNode::appendChild(NodePtr pNewNode)
{
    insertChild(pNewNode, unsigned(m_Children.size()));
}

void DivNode::insertChildBefore(NodePtr pNewNode, NodePtr pOldChild)
{
    if (!pOldChild) {
        throw Exception(AVG_ERR_NO_NODE,
                getID()+"::insertChildBefore called without a node.");
    }
    unsigned i = indexOf(pOldChild);
    insertChild(pNewNode, i);
}

void DivNode::insertChildAfter(NodePtr pNewNode, NodePtr pOldChild)
{
    if (!pOldChild) {
        throw Exception(AVG_ERR_NO_NODE,
                getID()+"::insertChildBefore called without a node.");
    }
    unsigned i = indexOf(pOldChild);
    insertChild(pNewNode, i+1);
}

void DivNode::insertChild(NodePtr pNode, unsigned i)
{
    if (!pNode) {
        throw Exception(AVG_ERR_NO_NODE,
                getID()+"::insertChild called without a node.");
    }
    if (pNode->getState() == NS_CONNECTED || pNode->getState() == NS_CANRENDER) {
        throw(Exception(AVG_ERR_ALREADY_CONNECTED,
                "Can't connect node with id "+pNode->getID()+
                ": already connected."));
    }
    if (getState() == NS_CONNECTED || getState() == NS_CANRENDER) {
        getCanvas()->registerNode(pNode);
    }
    DivNodePtr ptr = dynamic_pointer_cast<DivNode>(getThis());
    pNode->checkSetParentError(ptr);
    if (!isChildTypeAllowed(pNode->getTypeStr())) {
        throw(Exception(AVG_ERR_ALREADY_CONNECTED,
                "Can't insert a node of type "+pNode->getTypeStr()+
                " into a node of type "+getTypeStr()+"."));
    }
    if (i > m_Children.size()) {
        throw(Exception(AVG_ERR_OUT_OF_RANGE,
                pNode->getID()+"::insertChild: index out of bounds."));
    }
    std::vector<NodePtr>::iterator pos = m_Children.begin()+i;
    m_Children.insert(pos, pNode);
    pNode->setParent(ptr, getState(), getCanvas());
    if (getState() == NS_CANRENDER) {
        pNode->connectDisplay();
    }
}

void DivNode::reorderChild(NodePtr pNode, unsigned j)
{
    if (j > m_Children.size()-1) {
        throw(Exception(AVG_ERR_OUT_OF_RANGE,
                getID()+"::reorderChild: index "+toString(j)+" out of bounds."));
    }
    int i = indexOf(pNode);
    m_Children.erase(m_Children.begin()+i);
    std::vector<NodePtr>::iterator pos = m_Children.begin()+j;
    m_Children.insert(pos, pNode);
}

void DivNode::reorderChild(unsigned i, unsigned j)
{
    if (i > m_Children.size()-1 || j > m_Children.size()-1) {
        throw(Exception(AVG_ERR_OUT_OF_RANGE,
                getID()+"::reorderChild: index out of bounds."));
    }
    NodePtr pNode = getChild(i);
    m_Children.erase(m_Children.begin()+i);
    std::vector<NodePtr>::iterator pos = m_Children.begin()+j;
    m_Children.insert(pos, pNode);
}

unsigned DivNode::indexOf(NodePtr pChild)
{
    if (!pChild) {
        throw Exception(AVG_ERR_NO_NODE,
                getID()+"::indexOf called without a node.");
    }
    for (unsigned i = 0; i < m_Children.size(); ++i) {
        if (m_Children[i] == pChild) {
            return i;
        }
    }
    throw(Exception(AVG_ERR_OUT_OF_RANGE,
            "indexOf: node '"+pChild->getID()+"' is not a child of node '"
            +getID()+"'"));
}

void DivNode::removeChild(NodePtr pNode)
{
    removeChild(pNode, false);
}

void DivNode::removeChild(unsigned i)
{
    removeChild(i, false);
}

void DivNode::removeChild(NodePtr pNode, bool bKill)
{
    pNode->removeParent();
    if (pNode->getState() != NS_UNCONNECTED) {
        pNode->disconnect(bKill);
    }
    unsigned i = indexOf(pNode);
    if (i > m_Children.size()-1) {
        throw(Exception(AVG_ERR_OUT_OF_RANGE,
                getID()+"::removeChild: index "+toString(i)+" out of bounds."));
    }
    m_Children.erase(m_Children.begin()+i);
}

void DivNode::removeChild(unsigned i, bool bKill)
{
    NodePtr pNode = getChild(i);
    removeChild(pNode, bKill);
}

bool DivNode::getCrop() const
{
    return m_bCrop;
}

void DivNode::setCrop(bool bCrop)
{
    m_bCrop = bCrop;
}

const std::string& DivNode::getElementOutlineColor() const
{
    return m_sElementOutlineColor;
}

void DivNode::setElementOutlineColor(const std::string& sColor)
{
    m_sElementOutlineColor = sColor;
    if (sColor == "") {
        m_ElementOutlineColor = Pixel32(0,0,0,0);
    } else {
        m_ElementOutlineColor = colorStringToColor(m_sElementOutlineColor);
    }
}

const UTF8String& DivNode::getMediaDir() const
{
    return m_sMediaDir;
}

void DivNode::setMediaDir(const UTF8String& sMediaDir)
{
    m_sMediaDir = sMediaDir;
    checkReload();
}

void DivNode::getElementsByPos(const DPoint& pos, vector<NodeWeakPtr>& pElements)
{
    if (reactsToMouseEvents() &&
            ((getSize() == DPoint(DEFAULT_SIZE, DEFAULT_SIZE) ||
             (pos.x >= 0 && pos.y >= 0 && pos.x < getSize().x && pos.y < getSize().y))))
    {
        for (int i = getNumChildren()-1; i >= 0; i--) {
            NodePtr pCurChild = getChild(i);
            DPoint relPos = pCurChild->toLocal(pos);
            pCurChild->getElementsByPos(relPos, pElements);
            if (!pElements.empty()) {
                pElements.push_back(getThis());
                return;
            }
        }
        // pos isn't in any of the children.
        if (getSize() != DPoint(DEFAULT_SIZE, DEFAULT_SIZE)) {
            // Explicit width/height given for div - div reacts on its own.
            pElements.push_back(getThis());
        }
    }
}

void DivNode::preRender()
{
    Node::preRender();
    for (unsigned i = 0; i < getNumChildren(); i++) {
        getChild(i)->preRender();
    }
}

void DivNode::render(const DRect& rect)
{
    DPoint viewport = getSize();

    m_pClipVertexes->reset();
    m_pClipVertexes->appendPos(DPoint(0,0), DPoint(0,0), Pixel32(0,0,0,0));
    m_pClipVertexes->appendPos(DPoint(0,viewport.y), DPoint(0,0), Pixel32(0,0,0,0));
    m_pClipVertexes->appendPos(DPoint(viewport.x,0), DPoint(0,0), Pixel32(0,0,0,0));
    m_pClipVertexes->appendPos(viewport, DPoint(0,0), Pixel32(0,0,0,0));
    m_pClipVertexes->appendQuadIndexes(0, 1, 2, 3);

    if (getCrop()) {
        getCanvas()->pushClipRect(m_pClipVertexes);
    }
    for (unsigned i = 0; i < getNumChildren(); i++) {
        getChild(i)->maybeRender(rect);
    }
    if (getCrop()) {
        getCanvas()->popClipRect(m_pClipVertexes);
    }
}

void DivNode::renderOutlines(const VertexArrayPtr& pVA, Pixel32 color)
{
    Pixel32 effColor = color;
    if (m_ElementOutlineColor != Pixel32(0,0,0,0)) {
        effColor = m_ElementOutlineColor;
        effColor.setA(128);
    }
    if (effColor != Pixel32(0,0,0,0)) {
        DPoint size = getSize();
        if (size == DPoint(DEFAULT_SIZE, DEFAULT_SIZE)) {
            DPoint p0 = getAbsPos(DPoint(-4, 0.5));
            DPoint p1 = getAbsPos(DPoint(5, 0.5));
            DPoint p2 = getAbsPos(DPoint(0.5, -4));
            DPoint p3 = getAbsPos(DPoint(0.5, 5));
            pVA->addLineData(effColor, p0, p1, 1);
            pVA->addLineData(effColor, p2, p3, 1);
        } else {
            DPoint p0 = getAbsPos(DPoint(0.5, 0.5));
            DPoint p1 = getAbsPos(DPoint(size.x+0.5,0.5));
            DPoint p2 = getAbsPos(DPoint(size.x+0.5,size.y+0.5));
            DPoint p3 = getAbsPos(DPoint(0.5,size.y+0.5));
            pVA->addLineData(effColor, p0, p1, 1);
            pVA->addLineData(effColor, p1, p2, 1);
            pVA->addLineData(effColor, p2, p3, 1);
            pVA->addLineData(effColor, p3, p0, 1);
        }
    }
    for (unsigned i = 0; i < getNumChildren(); i++) {
        getChild(i)->renderOutlines(pVA, effColor);
    }
}

string DivNode::getEffectiveMediaDir()
{
    // TODO: There is no test for this function.
    string sMediaDir = m_sMediaDir;
    if (!isAbsPath(sMediaDir)) {
        if (getParent()) {
            sMediaDir = getParent()->getEffectiveMediaDir()+m_sMediaDir;
        } else {
            sMediaDir = Player::get()->getRootMediaDir()+m_sMediaDir;
        }
    }
    if (sMediaDir[sMediaDir.length()-1] != '/') {
        sMediaDir += '/';
    }
    return sMediaDir;
}

void DivNode::checkReload()
{
    for(unsigned i = 0; i < getNumChildren(); ++i) {
        getChild(i)->checkReload();
    }
}

string DivNode::dump(int indent)
{
    string dumpStr = AreaNode::dump () + "\n";
    vector<NodePtr>::iterator it;
    for(unsigned i = 0; i < getNumChildren(); ++i) {
        getChild(i)->dump(indent+2)+"\n";
    }
    return dumpStr;
}

IntPoint DivNode::getMediaSize()
{
    return IntPoint(DEFAULT_SIZE, DEFAULT_SIZE);
}

bool DivNode::isChildTypeAllowed(const string& sType)
{
    return getDefinition()->isChildAllowed(sType);
}

}
<commit_msg>DivNode cosmetics.<commit_after>//
//  libavg - Media Playback Engine.
//  Copyright (C) 2003-2011 Ulrich von Zadow
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//  Current versions can be found at www.libavg.de
//

#include "DivNode.h"
#include "Player.h"
#include "NodeDefinition.h"
#include "Canvas.h"

#include "../base/Point.h"
#include "../base/Exception.h"
#include "../base/Logger.h"
#include "../base/StringHelper.h"
#include "../base/FileHelper.h"
#include "../base/MathHelper.h"
#include "../base/ObjectCounter.h"

#include <iostream>
#include <sstream>
#include <limits>

using namespace std;
using namespace boost;

#define DEFAULT_SIZE 100000

namespace avg {

NodeDefinition DivNode::createDefinition()
{
    string sChildArray[] = {"image", "div", "canvas", "words", "video", "camera",
            "panoimage", "sound", "line", "rect", "curve", "polyline", "polygon",
            "circle", "mesh"};
    vector<string> sChildren = vectorFromCArray(
            sizeof(sChildArray) / sizeof(*sChildArray), sChildArray);
    return NodeDefinition("div", Node::buildNode<DivNode>)
        .extendDefinition(AreaNode::createDefinition())
        .addChildren(sChildren)
        .addArg(Arg<bool>("crop", false, false, offsetof(DivNode, m_bCrop)))
        .addArg(Arg<string>("elementoutlinecolor", "", false,
                offsetof(DivNode, m_sElementOutlineColor)))
        .addArg(Arg<UTF8String>("mediadir", "", false, offsetof(DivNode, m_sMediaDir)));
}

DivNode::DivNode(const ArgList& args)
{
    args.setMembers(this);
    setElementOutlineColor(m_sElementOutlineColor);
    ObjectCounter::get()->incRef(&typeid(*this));
}

DivNode::~DivNode()
{
    ObjectCounter::get()->decRef(&typeid(*this));
}

void DivNode::connectDisplay()
{
    AreaNode::connectDisplay();
    for (unsigned i = 0; i < getNumChildren(); ++i) {
        getChild(i)->connectDisplay();
    }
    m_pClipVertexes = VertexArrayPtr(new VertexArray());
}

void DivNode::connect(CanvasPtr pCanvas)
{
    AreaNode::connect(pCanvas);
    for (unsigned i = 0; i < getNumChildren(); ++i) {
        getChild(i)->connect(pCanvas);
    }
}

void DivNode::disconnect(bool bKill)
{
    for (unsigned i = 0; i < getNumChildren(); ++i) {
        getChild(i)->disconnect(bKill);
    }
    if (m_pClipVertexes) {
        m_pClipVertexes = VertexArrayPtr();
    }
    AreaNode::disconnect(bKill);
}

DPoint DivNode::getPivot() const
{
    DPoint pivot = AreaNode::getPivot();
    if (pivot == DPoint(DEFAULT_SIZE / 2, DEFAULT_SIZE / 2)) {
        return DPoint(0, 0);
    }
    return pivot;
}

unsigned DivNode::getNumChildren()
{
    return m_Children.size();
}

const NodePtr& DivNode::getChild(unsigned i)
{
    if (i >= m_Children.size()) {
        stringstream s;
        s << "Index " << i << " is out of range in Node::getChild()";
        throw(Exception(AVG_ERR_OUT_OF_RANGE, s.str()));
    }
    return m_Children[i];
}

void DivNode::appendChild(NodePtr pNewNode)
{
    insertChild(pNewNode, unsigned(m_Children.size()));
}

void DivNode::insertChildBefore(NodePtr pNewNode, NodePtr pOldChild)
{
    if (!pOldChild) {
        throw Exception(AVG_ERR_NO_NODE,
                getID()+"::insertChildBefore called without a node.");
    }
    unsigned i = indexOf(pOldChild);
    insertChild(pNewNode, i);
}

void DivNode::insertChildAfter(NodePtr pNewNode, NodePtr pOldChild)
{
    if (!pOldChild) {
        throw Exception(AVG_ERR_NO_NODE,
                getID()+"::insertChildBefore called without a node.");
    }
    unsigned i = indexOf(pOldChild);
    insertChild(pNewNode, i+1);
}

void DivNode::insertChild(NodePtr pChild, unsigned i)
{
    if (!pChild) {
        throw Exception(AVG_ERR_NO_NODE,
                getID()+"::insertChild called without a node.");
    }
    if (pChild->getState() == NS_CONNECTED || pChild->getState() == NS_CANRENDER) {
        throw(Exception(AVG_ERR_ALREADY_CONNECTED,
                "Can't connect node with id "+pChild->getID()+
                ": already connected."));
    }
    if (getState() == NS_CONNECTED || getState() == NS_CANRENDER) {
        getCanvas()->registerNode(pChild);
    }
    DivNodePtr ptr = dynamic_pointer_cast<DivNode>(getThis());
    pChild->checkSetParentError(ptr);
    if (!isChildTypeAllowed(pChild->getTypeStr())) {
        throw(Exception(AVG_ERR_ALREADY_CONNECTED,
                "Can't insert a node of type "+pChild->getTypeStr()+
                " into a node of type "+getTypeStr()+"."));
    }
    if (i > m_Children.size()) {
        throw(Exception(AVG_ERR_OUT_OF_RANGE,
                pChild->getID()+"::insertChild: index out of bounds."));
    }
    std::vector<NodePtr>::iterator pos = m_Children.begin()+i;
    m_Children.insert(pos, pChild);
    pChild->setParent(ptr, getState(), getCanvas());
    if (getState() == NS_CANRENDER) {
        pChild->connectDisplay();
    }
}

void DivNode::reorderChild(NodePtr pChild, unsigned j)
{
    if (j > m_Children.size()-1) {
        throw(Exception(AVG_ERR_OUT_OF_RANGE,
                getID()+"::reorderChild: index "+toString(j)+" out of bounds."));
    }
    int i = indexOf(pChild);
    m_Children.erase(m_Children.begin()+i);
    std::vector<NodePtr>::iterator pos = m_Children.begin()+j;
    m_Children.insert(pos, pChild);
}

void DivNode::reorderChild(unsigned i, unsigned j)
{
    if (i > m_Children.size()-1 || j > m_Children.size()-1) {
        throw(Exception(AVG_ERR_OUT_OF_RANGE,
                getID()+"::reorderChild: index out of bounds."));
    }
    NodePtr pChild = getChild(i);
    m_Children.erase(m_Children.begin()+i);
    std::vector<NodePtr>::iterator pos = m_Children.begin()+j;
    m_Children.insert(pos, pChild);
}

unsigned DivNode::indexOf(NodePtr pChild)
{
    if (!pChild) {
        throw Exception(AVG_ERR_NO_NODE,
                getID()+"::indexOf called without a node.");
    }
    for (unsigned i = 0; i < m_Children.size(); ++i) {
        if (m_Children[i] == pChild) {
            return i;
        }
    }
    throw(Exception(AVG_ERR_OUT_OF_RANGE,
            "indexOf: node '"+pChild->getID()+"' is not a child of node '"
            +getID()+"'"));
}

void DivNode::removeChild(NodePtr pChild)
{
    removeChild(pChild, false);
}

void DivNode::removeChild(unsigned i)
{
    removeChild(i, false);
}

void DivNode::removeChild(NodePtr pChild, bool bKill)
{
    pChild->removeParent();
    if (pChild->getState() != NS_UNCONNECTED) {
        pChild->disconnect(bKill);
    }
    unsigned i = indexOf(pChild);
    if (i > m_Children.size()-1) {
        throw(Exception(AVG_ERR_OUT_OF_RANGE,
                getID()+"::removeChild: index "+toString(i)+" out of bounds."));
    }
    m_Children.erase(m_Children.begin()+i);
}

void DivNode::removeChild(unsigned i, bool bKill)
{
    NodePtr pChild = getChild(i);
    removeChild(pChild, bKill);
}

bool DivNode::getCrop() const
{
    return m_bCrop;
}

void DivNode::setCrop(bool bCrop)
{
    m_bCrop = bCrop;
}

const std::string& DivNode::getElementOutlineColor() const
{
    return m_sElementOutlineColor;
}

void DivNode::setElementOutlineColor(const std::string& sColor)
{
    m_sElementOutlineColor = sColor;
    if (sColor == "") {
        m_ElementOutlineColor = Pixel32(0,0,0,0);
    } else {
        m_ElementOutlineColor = colorStringToColor(m_sElementOutlineColor);
    }
}

const UTF8String& DivNode::getMediaDir() const
{
    return m_sMediaDir;
}

void DivNode::setMediaDir(const UTF8String& sMediaDir)
{
    m_sMediaDir = sMediaDir;
    checkReload();
}

void DivNode::getElementsByPos(const DPoint& pos, vector<NodeWeakPtr>& pElements)
{
    if (reactsToMouseEvents() &&
            ((getSize() == DPoint(DEFAULT_SIZE, DEFAULT_SIZE) ||
             (pos.x >= 0 && pos.y >= 0 && pos.x < getSize().x && pos.y < getSize().y))))
    {
        for (int i = getNumChildren()-1; i >= 0; i--) {
            NodePtr pCurChild = getChild(i);
            DPoint relPos = pCurChild->toLocal(pos);
            pCurChild->getElementsByPos(relPos, pElements);
            if (!pElements.empty()) {
                pElements.push_back(getThis());
                return;
            }
        }
        // pos isn't in any of the children.
        if (getSize() != DPoint(DEFAULT_SIZE, DEFAULT_SIZE)) {
            // Explicit width/height given for div - div reacts on its own.
            pElements.push_back(getThis());
        }
    }
}

void DivNode::preRender()
{
    Node::preRender();
    for (unsigned i = 0; i < getNumChildren(); i++) {
        getChild(i)->preRender();
    }
}

void DivNode::render(const DRect& rect)
{
    DPoint viewport = getSize();

    m_pClipVertexes->reset();
    m_pClipVertexes->appendPos(DPoint(0,0), DPoint(0,0), Pixel32(0,0,0,0));
    m_pClipVertexes->appendPos(DPoint(0,viewport.y), DPoint(0,0), Pixel32(0,0,0,0));
    m_pClipVertexes->appendPos(DPoint(viewport.x,0), DPoint(0,0), Pixel32(0,0,0,0));
    m_pClipVertexes->appendPos(viewport, DPoint(0,0), Pixel32(0,0,0,0));
    m_pClipVertexes->appendQuadIndexes(0, 1, 2, 3);

    if (getCrop()) {
        getCanvas()->pushClipRect(m_pClipVertexes);
    }
    for (unsigned i = 0; i < getNumChildren(); i++) {
        getChild(i)->maybeRender(rect);
    }
    if (getCrop()) {
        getCanvas()->popClipRect(m_pClipVertexes);
    }
}

void DivNode::renderOutlines(const VertexArrayPtr& pVA, Pixel32 color)
{
    Pixel32 effColor = color;
    if (m_ElementOutlineColor != Pixel32(0,0,0,0)) {
        effColor = m_ElementOutlineColor;
        effColor.setA(128);
    }
    if (effColor != Pixel32(0,0,0,0)) {
        DPoint size = getSize();
        if (size == DPoint(DEFAULT_SIZE, DEFAULT_SIZE)) {
            DPoint p0 = getAbsPos(DPoint(-4, 0.5));
            DPoint p1 = getAbsPos(DPoint(5, 0.5));
            DPoint p2 = getAbsPos(DPoint(0.5, -4));
            DPoint p3 = getAbsPos(DPoint(0.5, 5));
            pVA->addLineData(effColor, p0, p1, 1);
            pVA->addLineData(effColor, p2, p3, 1);
        } else {
            DPoint p0 = getAbsPos(DPoint(0.5, 0.5));
            DPoint p1 = getAbsPos(DPoint(size.x+0.5,0.5));
            DPoint p2 = getAbsPos(DPoint(size.x+0.5,size.y+0.5));
            DPoint p3 = getAbsPos(DPoint(0.5,size.y+0.5));
            pVA->addLineData(effColor, p0, p1, 1);
            pVA->addLineData(effColor, p1, p2, 1);
            pVA->addLineData(effColor, p2, p3, 1);
            pVA->addLineData(effColor, p3, p0, 1);
        }
    }
    for (unsigned i = 0; i < getNumChildren(); i++) {
        getChild(i)->renderOutlines(pVA, effColor);
    }
}

string DivNode::getEffectiveMediaDir()
{
    // TODO: There is no test for this function.
    string sMediaDir = m_sMediaDir;
    if (!isAbsPath(sMediaDir)) {
        if (getParent()) {
            sMediaDir = getParent()->getEffectiveMediaDir()+m_sMediaDir;
        } else {
            sMediaDir = Player::get()->getRootMediaDir()+m_sMediaDir;
        }
    }
    if (sMediaDir[sMediaDir.length()-1] != '/') {
        sMediaDir += '/';
    }
    return sMediaDir;
}

void DivNode::checkReload()
{
    for(unsigned i = 0; i < getNumChildren(); ++i) {
        getChild(i)->checkReload();
    }
}

string DivNode::dump(int indent)
{
    string dumpStr = AreaNode::dump () + "\n";
    vector<NodePtr>::iterator it;
    for(unsigned i = 0; i < getNumChildren(); ++i) {
        getChild(i)->dump(indent+2)+"\n";
    }
    return dumpStr;
}

IntPoint DivNode::getMediaSize()
{
    return IntPoint(DEFAULT_SIZE, DEFAULT_SIZE);
}

bool DivNode::isChildTypeAllowed(const string& sType)
{
    return getDefinition()->isChildAllowed(sType);
}

}
<|endoftext|>


<commit_before>//
//  libavg - Media Playback Engine.
//  Copyright (C) 2003-2006 Ulrich von Zadow
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//  Current versions can be found at www.libavg.de
//

#include "OGLTile.h"
#include "../base/Logger.h"
#include "../base/Exception.h"
#include "../base/ScopeTimer.h"

#include <iostream>
#include <string>

namespace avg {

using namespace std;

OGLTile::OGLTile(IntRect Extent, IntPoint TexSize, int Stride, PixelFormat pf,
        SDLDisplayEngine * pEngine)
    : m_Extent(Extent),
      m_TexSize(TexSize),
      m_pf(pf),
      m_pEngine(pEngine)
{
    if (m_pf == YCbCr420p) {
        createTexture(0, m_TexSize, Stride, I8);
        createTexture(1, m_TexSize/2, Stride/2, I8);
        createTexture(2, m_TexSize/2, Stride/2, I8);
    } else {
        createTexture(0, m_TexSize, Stride, m_pf);
    }
}

OGLTile::~OGLTile()
{
    if (m_pf == YCbCr420p) {
        glDeleteTextures(1, &m_TexID[0]);
        glDeleteTextures(2, &m_TexID[0]);
        glDeleteTextures(3, &m_TexID[0]);
    } else {
        glDeleteTextures(1, &m_TexID[0]);
    }
    OGLErrorCheck(AVG_ERR_VIDEO_GENERAL, "OGLTile::~OGLTile: glDeleteTextures()");
}

const IntRect& OGLTile::getExtent() const
{
    return m_Extent;
}

const IntPoint& OGLTile::getTexSize() const
{
    return m_TexSize;
}

int OGLTile::getTexID(int i) const
{
    return m_TexID[i];
}

void OGLTile::blt(const DPoint& TLPoint, const DPoint& TRPoint,
        const DPoint& BLPoint, const DPoint& BRPoint) const
{
    double TexWidth;
    double TexHeight;
    int TextureMode = m_pEngine->getTextureMode();

    if (TextureMode == GL_TEXTURE_2D) {
        TexWidth = double(m_Extent.Width())/m_TexSize.x;
        TexHeight = double(m_Extent.Height())/m_TexSize.y;
    } else {
        TexWidth = m_TexSize.x;
        TexHeight = m_TexSize.y;
    }

    if (m_pf == YCbCr420p) {
        GLhandleARB hProgram = m_pEngine->getYCbCr420pShader()->getProgram();
        glproc::UseProgramObject(hProgram);
        OGLErrorCheck(AVG_ERR_VIDEO_GENERAL, "OGLTile::blt: glUseProgramObject()");
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(TextureMode, m_TexID[0]);
        glproc::Uniform1i(glproc::GetUniformLocation(hProgram, "YTexture"), 0);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(TextureMode, m_TexID[1]);
        glproc::Uniform1i(glproc::GetUniformLocation(hProgram, "CbTexture"), 1);
        glActiveTexture(GL_TEXTURE2);
        glBindTexture(TextureMode, m_TexID[2]);
        glproc::Uniform1i(glproc::GetUniformLocation(hProgram, "CrTexture"), 2);
        OGLErrorCheck(AVG_ERR_VIDEO_GENERAL, "OGLTile::blt: glUniform1i()");
    } else {
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(TextureMode, m_TexID[0]);
        if (m_pEngine->getYCbCrMode() == SDLDisplayEngine::OGL_SHADER) {
            glproc::UseProgramObject(0);
        }
    }
    glBegin(GL_QUADS);
    glTexCoord2d(0.0, 0.0);
    glVertex3d (TLPoint.x, TLPoint.y, 0.0);
    glTexCoord2d(TexWidth, 0.0);
    glVertex3d (TRPoint.x, TRPoint.y, 0.0);
    glTexCoord2d(TexWidth, TexHeight);
    glVertex3d (BRPoint.x, BRPoint.y, 0.0);
    glTexCoord2d(0.0, TexHeight);
    glVertex3d (BLPoint.x, BLPoint.y, 0.0);
    glEnd();
    OGLErrorCheck(AVG_ERR_VIDEO_GENERAL, "OGLTile::blt: glEnd()");
    if (m_pf == YCbCr420p) {
        glActiveTexture(GL_TEXTURE1);
        glDisable(TextureMode);
        glActiveTexture(GL_TEXTURE2);
        glDisable(TextureMode);
        glActiveTexture(GL_TEXTURE0);
        glproc::UseProgramObject(0);
        OGLErrorCheck(AVG_ERR_VIDEO_GENERAL, "OGLTile::blt: glDisable(TextureMode)");
    }
}

void OGLTile::createTexture(int i, IntPoint Size, int Stride, PixelFormat pf)
{
    glGenTextures(1, &m_TexID[i]);
    OGLErrorCheck(AVG_ERR_VIDEO_GENERAL, "OGLTile::createTexture: glGenTextures()");
    glActiveTexture(GL_TEXTURE0+i);
    int TextureMode = m_pEngine->getTextureMode();
    glBindTexture(TextureMode, m_TexID[i]);
    OGLErrorCheck(AVG_ERR_VIDEO_GENERAL, "OGLTile::createTexture: glBindTexture()");
    glTexParameteri(TextureMode, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(TextureMode, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(TextureMode, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(TextureMode, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    OGLErrorCheck(AVG_ERR_VIDEO_GENERAL,
            "OGLTile::createTexture: glTexParameteri()");
    glPixelStorei(GL_UNPACK_ROW_LENGTH, Size.x);
    OGLErrorCheck(AVG_ERR_VIDEO_GENERAL,
            "OGLTile::createTexture: glPixelStorei(GL_UNPACK_ROW_LENGTH)");

    GLenum DestMode = m_pEngine->getOGLDestMode(pf);
#ifdef __APPLE__
    // XXX: Hack to work around broken Mac OS X GL_ALPHA/GL_UNPACK_ROW_LENGTH.
    // If this is gone, the Stride parameter can be removed too :-).
    if (Stride != Size.x && DestMode == GL_ALPHA && m_pf == YCbCr420p) {
        DestMode = GL_RGBA;
    }
#endif
    char * pPixels = 0;
    if (TextureMode == GL_TEXTURE_2D) {
        // Make sure the texture is transparent and black before loading stuff
        // into it to avoid garbage at the borders.
        int TexMemNeeded = Size.x*Size.y*Bitmap::getBytesPerPixel(pf);
        pPixels = new char[TexMemNeeded];
        memset(pPixels, 0, TexMemNeeded);
    }
    glTexImage2D(TextureMode, 0, DestMode, Size.x, Size.y, 0,
            m_pEngine->getOGLSrcMode(pf), m_pEngine->getOGLPixelType(pf), pPixels);
    OGLErrorCheck(AVG_ERR_VIDEO_GENERAL,
            "OGLTile::createTexture: glTexImage2D()");
    if (TextureMode == GL_TEXTURE_2D) {
        free(pPixels);
    }
}

static ProfilingZone TexSubImageProfilingZone("    OGLTile::texture download");

void OGLTile::downloadTexture(int i, BitmapPtr pBmp, int stride,
                OGLMemoryMode MemoryMode) const
{
    PixelFormat pf;
    if (m_pf == YCbCr420p) {
        pf = I8;
    } else {
        pf = m_pf;
    }
    IntRect Extent = m_Extent;
    if (i != 0) {
        stride /= 2;
        Extent = IntRect(m_Extent.tl/2.0, m_Extent.br/2.0);
    }
    int TextureMode = m_pEngine->getTextureMode();
    glBindTexture(TextureMode, m_TexID[i]);
    OGLErrorCheck(AVG_ERR_VIDEO_GENERAL,
            "OGLTile::downloadTexture: glBindTexture()");
    int bpp = Bitmap::getBytesPerPixel(pf);
    glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
    OGLErrorCheck(AVG_ERR_VIDEO_GENERAL,
            "OGLTile::downloadTexture: glPixelStorei(GL_UNPACK_ROW_LENGTH)");
    unsigned char * pStartPos = (unsigned char *)
            (Extent.tl.y*stride*bpp + Extent.tl.x*bpp);
    if (MemoryMode == OGL) {
        pStartPos += (unsigned int)(pBmp->getPixels());
    }
    {
        ScopeTimer Timer(TexSubImageProfilingZone);
        glTexSubImage2D(TextureMode, 0, 0, 0, Extent.Width(), Extent.Height(),
                m_pEngine->getOGLSrcMode(pf), m_pEngine->getOGLPixelType(pf),
                pStartPos);
    }
    OGLErrorCheck(AVG_ERR_VIDEO_GENERAL,
            "OGLTile::downloadTexture: glTexSubImage2D()");

}

}
<commit_msg>x86_64 patch.<commit_after>//
//  libavg - Media Playback Engine.
//  Copyright (C) 2003-2006 Ulrich von Zadow
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//  Current versions can be found at www.libavg.de
//

#include "OGLTile.h"
#include "../base/Logger.h"
#include "../base/Exception.h"
#include "../base/ScopeTimer.h"

#include <iostream>
#include <string>

namespace avg {

using namespace std;

OGLTile::OGLTile(IntRect Extent, IntPoint TexSize, int Stride, PixelFormat pf,
        SDLDisplayEngine * pEngine)
    : m_Extent(Extent),
      m_TexSize(TexSize),
      m_pf(pf),
      m_pEngine(pEngine)
{
    if (m_pf == YCbCr420p) {
        createTexture(0, m_TexSize, Stride, I8);
        createTexture(1, m_TexSize/2, Stride/2, I8);
        createTexture(2, m_TexSize/2, Stride/2, I8);
    } else {
        createTexture(0, m_TexSize, Stride, m_pf);
    }
}

OGLTile::~OGLTile()
{
    if (m_pf == YCbCr420p) {
        glDeleteTextures(1, &m_TexID[0]);
        glDeleteTextures(2, &m_TexID[0]);
        glDeleteTextures(3, &m_TexID[0]);
    } else {
        glDeleteTextures(1, &m_TexID[0]);
    }
    OGLErrorCheck(AVG_ERR_VIDEO_GENERAL, "OGLTile::~OGLTile: glDeleteTextures()");
}

const IntRect& OGLTile::getExtent() const
{
    return m_Extent;
}

const IntPoint& OGLTile::getTexSize() const
{
    return m_TexSize;
}

int OGLTile::getTexID(int i) const
{
    return m_TexID[i];
}

void OGLTile::blt(const DPoint& TLPoint, const DPoint& TRPoint,
        const DPoint& BLPoint, const DPoint& BRPoint) const
{
    double TexWidth;
    double TexHeight;
    int TextureMode = m_pEngine->getTextureMode();

    if (TextureMode == GL_TEXTURE_2D) {
        TexWidth = double(m_Extent.Width())/m_TexSize.x;
        TexHeight = double(m_Extent.Height())/m_TexSize.y;
    } else {
        TexWidth = m_TexSize.x;
        TexHeight = m_TexSize.y;
    }

    if (m_pf == YCbCr420p) {
        GLhandleARB hProgram = m_pEngine->getYCbCr420pShader()->getProgram();
        glproc::UseProgramObject(hProgram);
        OGLErrorCheck(AVG_ERR_VIDEO_GENERAL, "OGLTile::blt: glUseProgramObject()");
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(TextureMode, m_TexID[0]);
        glproc::Uniform1i(glproc::GetUniformLocation(hProgram, "YTexture"), 0);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(TextureMode, m_TexID[1]);
        glproc::Uniform1i(glproc::GetUniformLocation(hProgram, "CbTexture"), 1);
        glActiveTexture(GL_TEXTURE2);
        glBindTexture(TextureMode, m_TexID[2]);
        glproc::Uniform1i(glproc::GetUniformLocation(hProgram, "CrTexture"), 2);
        OGLErrorCheck(AVG_ERR_VIDEO_GENERAL, "OGLTile::blt: glUniform1i()");
    } else {
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(TextureMode, m_TexID[0]);
        if (m_pEngine->getYCbCrMode() == SDLDisplayEngine::OGL_SHADER) {
            glproc::UseProgramObject(0);
        }
    }
    glBegin(GL_QUADS);
    glTexCoord2d(0.0, 0.0);
    glVertex3d (TLPoint.x, TLPoint.y, 0.0);
    glTexCoord2d(TexWidth, 0.0);
    glVertex3d (TRPoint.x, TRPoint.y, 0.0);
    glTexCoord2d(TexWidth, TexHeight);
    glVertex3d (BRPoint.x, BRPoint.y, 0.0);
    glTexCoord2d(0.0, TexHeight);
    glVertex3d (BLPoint.x, BLPoint.y, 0.0);
    glEnd();
    OGLErrorCheck(AVG_ERR_VIDEO_GENERAL, "OGLTile::blt: glEnd()");
    if (m_pf == YCbCr420p) {
        glActiveTexture(GL_TEXTURE1);
        glDisable(TextureMode);
        glActiveTexture(GL_TEXTURE2);
        glDisable(TextureMode);
        glActiveTexture(GL_TEXTURE0);
        glproc::UseProgramObject(0);
        OGLErrorCheck(AVG_ERR_VIDEO_GENERAL, "OGLTile::blt: glDisable(TextureMode)");
    }
}

void OGLTile::createTexture(int i, IntPoint Size, int Stride, PixelFormat pf)
{
    glGenTextures(1, &m_TexID[i]);
    OGLErrorCheck(AVG_ERR_VIDEO_GENERAL, "OGLTile::createTexture: glGenTextures()");
    glActiveTexture(GL_TEXTURE0+i);
    int TextureMode = m_pEngine->getTextureMode();
    glBindTexture(TextureMode, m_TexID[i]);
    OGLErrorCheck(AVG_ERR_VIDEO_GENERAL, "OGLTile::createTexture: glBindTexture()");
    glTexParameteri(TextureMode, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(TextureMode, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(TextureMode, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(TextureMode, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    OGLErrorCheck(AVG_ERR_VIDEO_GENERAL,
            "OGLTile::createTexture: glTexParameteri()");
    glPixelStorei(GL_UNPACK_ROW_LENGTH, Size.x);
    OGLErrorCheck(AVG_ERR_VIDEO_GENERAL,
            "OGLTile::createTexture: glPixelStorei(GL_UNPACK_ROW_LENGTH)");

    GLenum DestMode = m_pEngine->getOGLDestMode(pf);
#ifdef __APPLE__
    // XXX: Hack to work around broken Mac OS X GL_ALPHA/GL_UNPACK_ROW_LENGTH.
    // If this is gone, the Stride parameter can be removed too :-).
    if (Stride != Size.x && DestMode == GL_ALPHA && m_pf == YCbCr420p) {
        DestMode = GL_RGBA;
    }
#endif
    char * pPixels = 0;
    if (TextureMode == GL_TEXTURE_2D) {
        // Make sure the texture is transparent and black before loading stuff
        // into it to avoid garbage at the borders.
        int TexMemNeeded = Size.x*Size.y*Bitmap::getBytesPerPixel(pf);
        pPixels = new char[TexMemNeeded];
        memset(pPixels, 0, TexMemNeeded);
    }
    glTexImage2D(TextureMode, 0, DestMode, Size.x, Size.y, 0,
            m_pEngine->getOGLSrcMode(pf), m_pEngine->getOGLPixelType(pf), pPixels);
    OGLErrorCheck(AVG_ERR_VIDEO_GENERAL,
            "OGLTile::createTexture: glTexImage2D()");
    if (TextureMode == GL_TEXTURE_2D) {
        free(pPixels);
    }
}

static ProfilingZone TexSubImageProfilingZone("    OGLTile::texture download");

void OGLTile::downloadTexture(int i, BitmapPtr pBmp, int stride,
                OGLMemoryMode MemoryMode) const
{
    PixelFormat pf;
    if (m_pf == YCbCr420p) {
        pf = I8;
    } else {
        pf = m_pf;
    }
    IntRect Extent = m_Extent;
    if (i != 0) {
        stride /= 2;
        Extent = IntRect(m_Extent.tl/2.0, m_Extent.br/2.0);
    }
    int TextureMode = m_pEngine->getTextureMode();
    glBindTexture(TextureMode, m_TexID[i]);
    OGLErrorCheck(AVG_ERR_VIDEO_GENERAL,
            "OGLTile::downloadTexture: glBindTexture()");
    int bpp = Bitmap::getBytesPerPixel(pf);
    glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
    OGLErrorCheck(AVG_ERR_VIDEO_GENERAL,
            "OGLTile::downloadTexture: glPixelStorei(GL_UNPACK_ROW_LENGTH)");
    unsigned char * pStartPos = (unsigned char *)
            (Extent.tl.y*stride*bpp + Extent.tl.x*bpp);
    if (MemoryMode == OGL) {
        pStartPos += (unsigned long)(pBmp->getPixels());
    }
    {
        ScopeTimer Timer(TexSubImageProfilingZone);
        glTexSubImage2D(TextureMode, 0, 0, 0, Extent.Width(), Extent.Height(),
                m_pEngine->getOGLSrcMode(pf), m_pEngine->getOGLPixelType(pf),
                pStartPos);
    }
    OGLErrorCheck(AVG_ERR_VIDEO_GENERAL,
            "OGLTile::downloadTexture: glTexSubImage2D()");

}

}
<|endoftext|>


<commit_before>#include "zipper.h"
#include "defs.h"
#include "tools.h"
#include "CDirEntry.h"

#include <fstream>
#include <stdexcept>

namespace zipper {

struct Zipper::Impl
{
    Zipper& m_outer;
    zipFile m_zf;
    ourmemory_t m_zipmem;
    zlib_filefunc_def m_filefunc;

    Impl(Zipper& outer)
        : m_outer(outer), m_zipmem(), m_filefunc()
    {
        m_zf = NULL;
        m_zipmem.base = NULL;
        //m_filefunc = { 0 };
    }

    ~Impl()
    {
        close();
    }

    bool initFile(const std::string& filename)
    {
#ifdef USEWIN32IOAPI
        zlib_filefunc64_def ffunc = { 0 };
#endif

        int mode = 0;
        int flags = Zipper::Append;

        /* open the zip file for output */
        if (checkFileExists(filename))
        {
            mode = (flags & Zipper::Overwrite) ? APPEND_STATUS_CREATE : APPEND_STATUS_ADDINZIP;
        }
        else
        {
            mode = APPEND_STATUS_CREATE;
        }

#ifdef USEWIN32IOAPI
        fill_win32_filefunc64A(&ffunc);
        m_zf = zipOpen2_64(filename.c_str(), mode, NULL, &ffunc);
#else
        m_zf = zipOpen64(filename.c_str(), mode);
#endif

        return NULL != m_zf;
    }

    bool initWithStream(std::iostream& stream)
    {
        m_zipmem.grow = 1;

        stream.seekg(0, std::ios::end);
        std::streampos s = stream.tellg();
        if (s < 0)
        {
            return false;
        }
        size_t size = static_cast<size_t>(s);
        stream.seekg(0);

        if (size > 0)
        {
            if (m_zipmem.base != NULL)
            {
                free(m_zipmem.base);
            }
            m_zipmem.base = reinterpret_cast<char*>(malloc(size * sizeof(char)));
            stream.read(m_zipmem.base, std::streamsize(size));
        }

        fill_memory_filefunc(&m_filefunc, &m_zipmem);

        return initMemory(size > 0 ? APPEND_STATUS_CREATE : APPEND_STATUS_ADDINZIP, m_filefunc);
    }

    bool initWithVector(std::vector<unsigned char>& buffer)
    {
        m_zipmem.grow = 1;

        if (!buffer.empty())
        {
            if (m_zipmem.base != NULL)
            {
                free(m_zipmem.base);
            }
            m_zipmem.base = reinterpret_cast<char*>(malloc(buffer.size() * sizeof(char)));
            memcpy(m_zipmem.base, reinterpret_cast<char*>(buffer.data()), buffer.size());
            m_zipmem.size = static_cast<uLong>(buffer.size());
        }

        fill_memory_filefunc(&m_filefunc, &m_zipmem);

        return initMemory(buffer.empty() ? APPEND_STATUS_CREATE : APPEND_STATUS_ADDINZIP, m_filefunc);
    }

    bool initMemory(int mode, zlib_filefunc_def& filefunc)
    {
        m_zf = zipOpen3("__notused__", mode, 0, 0, &filefunc);
        return m_zf != NULL;
    }

    bool add(std::istream& input_stream, const std::tm& timestamp,
             const std::string& nameInZip, const std::string& password, int flags)
    {
        if (!m_zf)
            return false;

        int compressLevel = 0;
        bool zip64;
        size_t size_buf = WRITEBUFFERSIZE;
        int err = ZIP_OK;
        unsigned long crcFile = 0;

        zip_fileinfo zi;
        zi.tmz_date.tm_sec = uInt(timestamp.tm_sec);
        zi.tmz_date.tm_min = uInt(timestamp.tm_min);
        zi.tmz_date.tm_hour = uInt(timestamp.tm_hour);
        zi.tmz_date.tm_mday = uInt(timestamp.tm_mday);
        zi.tmz_date.tm_mon = uInt(timestamp.tm_mon);
        zi.tmz_date.tm_year = uInt(timestamp.tm_year);

        size_t size_read;

        std::vector<char> buff;
        buff.resize(size_buf);

        if (nameInZip.empty())
            return false;

        if (flags & Zipper::Faster)
            compressLevel = 1;
        if (flags & Zipper::Better)
            compressLevel = 9;

        zip64 = isLargeFile(input_stream);
        if (password.empty())
        {
            err = zipOpenNewFileInZip64(m_zf,
                                        nameInZip.c_str(),
                                        &zi,
                                        NULL,
                                        0,
                                        NULL,
                                        0,
                                        NULL /* comment*/,
                                        (compressLevel != 0) ? Z_DEFLATED : 0,
                                        compressLevel,
                                        zip64);
        }
        else
        {
            getFileCrc(input_stream, buff, crcFile);
            err = zipOpenNewFileInZip3_64(m_zf,
                                          nameInZip.c_str(),
                                          &zi,
                                          NULL,
                                          0,
                                          NULL,
                                          0,
                                          NULL /* comment*/,
                                          (compressLevel != 0) ? Z_DEFLATED : 0,
                                          compressLevel,
                                          0,
                                          /* -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, */
                                          -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
                                          password.c_str(),
                                          crcFile,
                                          zip64);
        }

        if (ZIP_OK == err)
        {
            do
            {
                err = ZIP_OK;
                input_stream.read(buff.data(), std::streamsize(buff.size()));
                size_read = static_cast<size_t>(input_stream.gcount());
                if (size_read < buff.size() && !input_stream.eof() && !input_stream.good())
                {
                    err = ZIP_ERRNO;
                }

                if (size_read > 0)
                {
                    err = zipWriteInFileInZip(this->m_zf, buff.data(), static_cast<unsigned int>(size_read));
                }
            } while ((err == ZIP_OK) && (size_read > 0));
        }
        else
        {
            throw EXCEPTION_CLASS(("Error adding '" + nameInZip + "' to zip").c_str());
        }

        if (ZIP_OK == err)
        {
            err = zipCloseFileInZip(this->m_zf);
        }

        return ZIP_OK == err;
    }

    void close()
    {
        if (m_zf != NULL)
        {
            zipClose(m_zf, NULL);
            m_zf = NULL;
        }

        if (m_zipmem.base && m_zipmem.limit > 0)
        {
            if (m_outer.m_usingMemoryVector)
            {
                m_outer.m_vecbuffer.resize(m_zipmem.limit);
                m_outer.m_vecbuffer.assign(m_zipmem.base, m_zipmem.base + m_zipmem.limit);
            }
            else if (m_outer.m_usingStream)
            {
                m_outer.m_obuffer.write(m_zipmem.base, std::streamsize(m_zipmem.limit));
            }
        }

        if (m_zipmem.base != NULL)
        {
            free(m_zipmem.base);
            m_zipmem.base = NULL;
        }
    }
};

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

Zipper::Zipper(const std::string& zipname, const std::string& password)
    : m_obuffer(*(new std::stringstream())) //not used but using local variable throws exception
    , m_vecbuffer(*(new std::vector<unsigned char>())) //not used but using local variable throws exception
    , m_zipname(zipname)
    , m_password(password)
    , m_usingMemoryVector(false)
    , m_usingStream(false)
    , m_impl(new Impl(*this))
{
    if (!m_impl->initFile(zipname))
    {
        release();
        throw EXCEPTION_CLASS("Error creating zip in file!");
    }
    m_open = true;
}

Zipper::Zipper(std::iostream& buffer, const std::string& password)
    : m_obuffer(buffer)
    , m_vecbuffer(*(new std::vector<unsigned char>())) //not used but using local variable throws exception
    , m_password(password)
    , m_usingMemoryVector(false)
    , m_usingStream(true)
    , m_impl(new Impl(*this))
{
    if (!m_impl->initWithStream(m_obuffer))
    {
        release();
        throw EXCEPTION_CLASS("Error creating zip in memory!");
    }
    m_open = true;
}

Zipper::Zipper(std::vector<unsigned char>& buffer, const std::string& password)
    : m_obuffer(*(new std::stringstream())) //not used but using local variable throws exception
    , m_vecbuffer(buffer)
    , m_password(password)
    , m_usingMemoryVector(true)
    , m_usingStream(false)
    , m_impl(new Impl(*this))
{
    if (!m_impl->initWithVector(m_vecbuffer))
    {
        release();
        throw EXCEPTION_CLASS("Error creating zip in memory!");
    }
    m_open = true;
}

Zipper::~Zipper()
{
    close();
    release();
}

void Zipper::release()
{
    if (!m_usingMemoryVector)
    {
        delete &m_vecbuffer;
    }
    if (!m_usingStream)
    {
        delete &m_obuffer;
    }
    delete m_impl;
}

bool Zipper::add(std::istream& source, const std::tm& timestamp, const std::string& nameInZip, zipFlags flags)
{
    return m_impl->add(source, timestamp, nameInZip, m_password, flags);
}

bool Zipper::add(std::istream& source, const std::string& nameInZip, zipFlags flags)
{
    return m_impl->add(source, {}, nameInZip, m_password, flags);
}

bool Zipper::add(const std::string& fileOrFolderPath, zipFlags flags)
{
    if (isDirectory(fileOrFolderPath))
    {
        std::string folderName = fileNameFromPath(fileOrFolderPath);
        std::vector<std::string> files = filesFromDirectory(fileOrFolderPath);
        std::vector<std::string>::iterator it = files.begin();
        for (; it != files.end(); ++it)
        {
            std::ifstream input(it->c_str(), std::ios::binary);
            std::string nameInZip = it->substr(it->rfind(folderName + CDirEntry::Separator), it->size());
            add(input, nameInZip, flags);
            input.close();
        }
    }
    else
    {
        std::ifstream input(fileOrFolderPath.c_str(), std::ios::binary);
        std::string fullFileName;

        if (flags & Zipper::SaveHierarchy)
        {
            fullFileName = fileOrFolderPath;
        }
        else
        {
            fullFileName = fileNameFromPath(fileOrFolderPath);
        }

        add(input, fullFileName, flags);

        input.close();
    }

    return true;
}

void Zipper::open()
{
    if (!m_open)
    {
        if (m_usingMemoryVector)
        {
            if (!m_impl->initWithVector(m_vecbuffer))
                throw EXCEPTION_CLASS("Error opening zip memory!");
        }
        else if (m_usingStream)
        {
            if (!m_impl->initWithStream(m_obuffer))
                throw EXCEPTION_CLASS("Error opening zip memory!");
        }
        else
        {
            if (!m_impl->initFile(m_zipname))
                throw EXCEPTION_CLASS("Error opening zip file!");
        }

        m_open = true;
    }
}

void Zipper::close()
{
    if (m_open)
    {
        m_impl->close();
        m_open = false;
    }
}

} // namespace zipper
<commit_msg>Fix valgrind false errors<commit_after>#include "zipper.h"
#include "defs.h"
#include "tools.h"
#include "CDirEntry.h"

#include <fstream>
#include <stdexcept>

namespace zipper {

struct Zipper::Impl
{
    Zipper& m_outer;
    zipFile m_zf;
    ourmemory_t m_zipmem;
    zlib_filefunc_def m_filefunc;

    Impl(Zipper& outer)
        : m_outer(outer), m_zipmem(), m_filefunc()
    {
        m_zf = NULL;
        m_zipmem.base = NULL;
        //m_filefunc = { 0 };
    }

    ~Impl()
    {
        close();
    }

    bool initFile(const std::string& filename)
    {
#ifdef USEWIN32IOAPI
        zlib_filefunc64_def ffunc = { 0 };
#endif

        int mode = 0;
        int flags = Zipper::Append;

        /* open the zip file for output */
        if (checkFileExists(filename))
        {
            mode = (flags & Zipper::Overwrite) ? APPEND_STATUS_CREATE : APPEND_STATUS_ADDINZIP;
        }
        else
        {
            mode = APPEND_STATUS_CREATE;
        }

#ifdef USEWIN32IOAPI
        fill_win32_filefunc64A(&ffunc);
        m_zf = zipOpen2_64(filename.c_str(), mode, NULL, &ffunc);
#else
        m_zf = zipOpen64(filename.c_str(), mode);
#endif

        return NULL != m_zf;
    }

    bool initWithStream(std::iostream& stream)
    {
        m_zipmem.grow = 1;

        stream.seekg(0, std::ios::end);
        std::streampos s = stream.tellg();
        if (s < 0)
        {
            return false;
        }
        size_t size = static_cast<size_t>(s);
        stream.seekg(0);

        if (size > 0)
        {
            if (m_zipmem.base != NULL)
            {
                free(m_zipmem.base);
            }
            m_zipmem.base = reinterpret_cast<char*>(malloc(size * sizeof(char)));
            stream.read(m_zipmem.base, std::streamsize(size));
        }

        fill_memory_filefunc(&m_filefunc, &m_zipmem);

        return initMemory(size > 0 ? APPEND_STATUS_CREATE : APPEND_STATUS_ADDINZIP, m_filefunc);
    }

    bool initWithVector(std::vector<unsigned char>& buffer)
    {
        m_zipmem.grow = 1;

        if (!buffer.empty())
        {
            if (m_zipmem.base != NULL)
            {
                free(m_zipmem.base);
            }
            m_zipmem.base = reinterpret_cast<char*>(malloc(buffer.size() * sizeof(char)));
            memcpy(m_zipmem.base, reinterpret_cast<char*>(buffer.data()), buffer.size());
            m_zipmem.size = static_cast<uLong>(buffer.size());
        }

        fill_memory_filefunc(&m_filefunc, &m_zipmem);

        return initMemory(buffer.empty() ? APPEND_STATUS_CREATE : APPEND_STATUS_ADDINZIP, m_filefunc);
    }

    bool initMemory(int mode, zlib_filefunc_def& filefunc)
    {
        m_zf = zipOpen3("__notused__", mode, 0, 0, &filefunc);
        return m_zf != NULL;
    }

    bool add(std::istream& input_stream, const std::tm& timestamp,
             const std::string& nameInZip, const std::string& password, int flags)
    {
        if (!m_zf)
            return false;

        int compressLevel = 0;
        bool zip64;
        size_t size_buf = WRITEBUFFERSIZE;
        int err = ZIP_OK;
        unsigned long crcFile = 0;

        zip_fileinfo zi;
        zi.dosDate = 0; // if dos_date == 0, tmz_date is used
        zi.internal_fa = 0; // internal file attributes
        zi.external_fa = 0; // external file attributes
        zi.tmz_date.tm_sec = uInt(timestamp.tm_sec);
        zi.tmz_date.tm_min = uInt(timestamp.tm_min);
        zi.tmz_date.tm_hour = uInt(timestamp.tm_hour);
        zi.tmz_date.tm_mday = uInt(timestamp.tm_mday);
        zi.tmz_date.tm_mon = uInt(timestamp.tm_mon);
        zi.tmz_date.tm_year = uInt(timestamp.tm_year);

        size_t size_read;

        std::vector<char> buff;
        buff.resize(size_buf);

        if (nameInZip.empty())
            return false;

        if (flags & Zipper::Faster)
            compressLevel = 1;
        if (flags & Zipper::Better)
            compressLevel = 9;

        zip64 = isLargeFile(input_stream);
        if (password.empty())
        {
            err = zipOpenNewFileInZip64(m_zf,
                                        nameInZip.c_str(),
                                        &zi,
                                        NULL,
                                        0,
                                        NULL,
                                        0,
                                        NULL /* comment*/,
                                        (compressLevel != 0) ? Z_DEFLATED : 0,
                                        compressLevel,
                                        zip64);
        }
        else
        {
            getFileCrc(input_stream, buff, crcFile);
            err = zipOpenNewFileInZip3_64(m_zf,
                                          nameInZip.c_str(),
                                          &zi,
                                          NULL,
                                          0,
                                          NULL,
                                          0,
                                          NULL /* comment*/,
                                          (compressLevel != 0) ? Z_DEFLATED : 0,
                                          compressLevel,
                                          0,
                                          /* -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, */
                                          -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
                                          password.c_str(),
                                          crcFile,
                                          zip64);
        }

        if (ZIP_OK == err)
        {
            do
            {
                err = ZIP_OK;
                input_stream.read(buff.data(), std::streamsize(buff.size()));
                size_read = static_cast<size_t>(input_stream.gcount());
                if (size_read < buff.size() && !input_stream.eof() && !input_stream.good())
                {
                    err = ZIP_ERRNO;
                }

                if (size_read > 0)
                {
                    err = zipWriteInFileInZip(this->m_zf, buff.data(), static_cast<unsigned int>(size_read));
                }
            } while ((err == ZIP_OK) && (size_read > 0));
        }
        else
        {
            throw EXCEPTION_CLASS(("Error adding '" + nameInZip + "' to zip").c_str());
        }

        if (ZIP_OK == err)
        {
            err = zipCloseFileInZip(this->m_zf);
        }

        return ZIP_OK == err;
    }

    void close()
    {
        if (m_zf != NULL)
        {
            zipClose(m_zf, NULL);
            m_zf = NULL;
        }

        if (m_zipmem.base && m_zipmem.limit > 0)
        {
            if (m_outer.m_usingMemoryVector)
            {
                m_outer.m_vecbuffer.resize(m_zipmem.limit);
                m_outer.m_vecbuffer.assign(m_zipmem.base, m_zipmem.base + m_zipmem.limit);
            }
            else if (m_outer.m_usingStream)
            {
                m_outer.m_obuffer.write(m_zipmem.base, std::streamsize(m_zipmem.limit));
            }
        }

        if (m_zipmem.base != NULL)
        {
            free(m_zipmem.base);
            m_zipmem.base = NULL;
        }
    }
};

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

Zipper::Zipper(const std::string& zipname, const std::string& password)
    : m_obuffer(*(new std::stringstream())) //not used but using local variable throws exception
    , m_vecbuffer(*(new std::vector<unsigned char>())) //not used but using local variable throws exception
    , m_zipname(zipname)
    , m_password(password)
    , m_usingMemoryVector(false)
    , m_usingStream(false)
    , m_impl(new Impl(*this))
{
    if (!m_impl->initFile(zipname))
    {
        release();
        throw EXCEPTION_CLASS("Error creating zip in file!");
    }
    m_open = true;
}

Zipper::Zipper(std::iostream& buffer, const std::string& password)
    : m_obuffer(buffer)
    , m_vecbuffer(*(new std::vector<unsigned char>())) //not used but using local variable throws exception
    , m_password(password)
    , m_usingMemoryVector(false)
    , m_usingStream(true)
    , m_impl(new Impl(*this))
{
    if (!m_impl->initWithStream(m_obuffer))
    {
        release();
        throw EXCEPTION_CLASS("Error creating zip in memory!");
    }
    m_open = true;
}

Zipper::Zipper(std::vector<unsigned char>& buffer, const std::string& password)
    : m_obuffer(*(new std::stringstream())) //not used but using local variable throws exception
    , m_vecbuffer(buffer)
    , m_password(password)
    , m_usingMemoryVector(true)
    , m_usingStream(false)
    , m_impl(new Impl(*this))
{
    if (!m_impl->initWithVector(m_vecbuffer))
    {
        release();
        throw EXCEPTION_CLASS("Error creating zip in memory!");
    }
    m_open = true;
}

Zipper::~Zipper()
{
    close();
    release();
}

void Zipper::release()
{
    if (!m_usingMemoryVector)
    {
        delete &m_vecbuffer;
    }
    if (!m_usingStream)
    {
        delete &m_obuffer;
    }
    delete m_impl;
}

bool Zipper::add(std::istream& source, const std::tm& timestamp, const std::string& nameInZip, zipFlags flags)
{
    return m_impl->add(source, timestamp, nameInZip, m_password, flags);
}

bool Zipper::add(std::istream& source, const std::string& nameInZip, zipFlags flags)
{
    return m_impl->add(source, {}, nameInZip, m_password, flags);
}

bool Zipper::add(const std::string& fileOrFolderPath, zipFlags flags)
{
    if (isDirectory(fileOrFolderPath))
    {
        std::string folderName = fileNameFromPath(fileOrFolderPath);
        std::vector<std::string> files = filesFromDirectory(fileOrFolderPath);
        std::vector<std::string>::iterator it = files.begin();
        for (; it != files.end(); ++it)
        {
            std::ifstream input(it->c_str(), std::ios::binary);
            std::string nameInZip = it->substr(it->rfind(folderName + CDirEntry::Separator), it->size());
            add(input, nameInZip, flags);
            input.close();
        }
    }
    else
    {
        std::ifstream input(fileOrFolderPath.c_str(), std::ios::binary);
        std::string fullFileName;

        if (flags & Zipper::SaveHierarchy)
        {
            fullFileName = fileOrFolderPath;
        }
        else
        {
            fullFileName = fileNameFromPath(fileOrFolderPath);
        }

        add(input, fullFileName, flags);

        input.close();
    }

    return true;
}

void Zipper::open()
{
    if (!m_open)
    {
        if (m_usingMemoryVector)
        {
            if (!m_impl->initWithVector(m_vecbuffer))
                throw EXCEPTION_CLASS("Error opening zip memory!");
        }
        else if (m_usingStream)
        {
            if (!m_impl->initWithStream(m_obuffer))
                throw EXCEPTION_CLASS("Error opening zip memory!");
        }
        else
        {
            if (!m_impl->initFile(m_zipname))
                throw EXCEPTION_CLASS("Error opening zip file!");
        }

        m_open = true;
    }
}

void Zipper::close()
{
    if (m_open)
    {
        m_impl->close();
        m_open = false;
    }
}

} // namespace zipper
<|endoftext|>


<commit_before><commit_msg>Disable BrowserFocusTest.BrowsersRememberFocus until it can be looked at.<commit_after><|endoftext|>


<commit_before>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/extensions/extension.h"

#include "base/logging.h"
#include "base/string_util.h"

const FilePath::CharType* Extension::kManifestFilename =
    FILE_PATH_LITERAL("manifest");

const wchar_t* Extension::kFormatVersionKey = L"format_version";
const wchar_t* Extension::kIdKey = L"id";
const wchar_t* Extension::kNameKey = L"name";
const wchar_t* Extension::kDescriptionKey = L"description";
const wchar_t* Extension::kContentScriptsKey = L"content_scripts";

const wchar_t* Extension::kInvalidManifestError =
    L"Manifest is missing or invalid.";
const wchar_t* Extension::kInvalidFormatVersionError =
    StringPrintf(L"Required key '%ls' is missing or invalid",
                 kFormatVersionKey).c_str();
const wchar_t* Extension::kInvalidIdError =
    StringPrintf(L"Required key '%ls' is missing or invalid.",
                 kIdKey).c_str();
const wchar_t* Extension::kInvalidNameError =
    StringPrintf(L"Required key '%ls' is missing or has invalid type.",
                 kNameKey).c_str();
const wchar_t* Extension::kInvalidDescriptionError =
    StringPrintf(L"Invalid type for '%ls' key.",
                 kDescriptionKey).c_str();
const wchar_t* Extension::kInvalidContentScriptsListError =
    StringPrintf(L"Invalid type for '%ls' key.",
                 kContentScriptsKey).c_str();
const wchar_t* Extension::kInvalidContentScriptError =
    StringPrintf(L"Invalid type for %ls at index ",
                 kContentScriptsKey).c_str();

bool Extension::InitFromValue(const DictionaryValue& source,
                              std::wstring* error) {
  // Check format version.
  int format_version = 0;
  if (!source.GetInteger(kFormatVersionKey, &format_version) ||
      format_version != kExpectedFormatVersion) {
    *error = kInvalidFormatVersionError;
    return false;
  }

  // Initialize id.
  if (!source.GetString(kIdKey, &id_)) {
    *error = kInvalidIdError;
    return false;
  }

  // Initialize name.
  if (!source.GetString(kNameKey, &name_)) {
    *error = kInvalidNameError;
    return false;
  }

  // Initialize description (optional).
  Value* value = NULL;
  if (source.Get(kDescriptionKey, &value)) {
    if (!value->GetAsString(&description_)) {
      *error = kInvalidDescriptionError;
      return false;
    }
  }

  // Initialize content scripts (optional).
  if (source.Get(kContentScriptsKey, &value)) {
    ListValue* list_value = NULL;
    if (value->GetType() != Value::TYPE_LIST) {
      *error = kInvalidContentScriptsListError;
      return false;
    } else {
      list_value = static_cast<ListValue*>(value);
    }

    for (size_t i = 0; i < list_value->GetSize(); ++i) {
      std::wstring content_script;
      if (!list_value->Get(i, &value) || !value->GetAsString(&content_script)) {
        *error = kInvalidContentScriptError;
        *error += IntToWString(i);
        return false;
      }

      content_scripts_.push_back(content_script);
    }
  }

  return true;
}

void Extension::CopyToValue(DictionaryValue* destination) {
  // Set format version
  destination->SetInteger(kFormatVersionKey,
                          kExpectedFormatVersion);

  // Copy id.
  destination->SetString(kIdKey, id_);

  // Copy name.
  destination->SetString(kNameKey, name_);

  // Copy description (optional).
  if (description_.size() > 0)
    destination->SetString(kDescriptionKey, description_);

  // Copy content scripts (optional).
  if (content_scripts_.size() > 0) {
    ListValue* list_value = new ListValue();
    destination->Set(kContentScriptsKey, list_value);

    for (size_t i = 0; i < content_scripts_.size(); ++i) {
      list_value->Set(i, Value::CreateStringValue(content_scripts_[i]));
    }
  }
}
<commit_msg>Fixed a bogus initialization of string constants.  The stack allocated string from StringPrintf gets destroyed right away, and c_str is just a pointer to its internal buffer, so the error constant was bogus memory.  This showed up in the purify tests.<commit_after>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/extensions/extension.h"

#include "base/logging.h"
#include "base/string_util.h"

const FilePath::CharType* Extension::kManifestFilename =
    FILE_PATH_LITERAL("manifest");

const wchar_t* Extension::kFormatVersionKey = L"format_version";
const wchar_t* Extension::kIdKey = L"id";
const wchar_t* Extension::kNameKey = L"name";
const wchar_t* Extension::kDescriptionKey = L"description";
const wchar_t* Extension::kContentScriptsKey = L"content_scripts";

const wchar_t* Extension::kInvalidManifestError =
    L"Manifest is missing or invalid.";
const wchar_t* Extension::kInvalidFormatVersionError =
    L"Required key 'format_version' is missing or invalid";
const wchar_t* Extension::kInvalidIdError =
    L"Required key 'id' is missing or invalid.";
const wchar_t* Extension::kInvalidNameError =
    L"Required key 'name' is missing or has invalid type.";
const wchar_t* Extension::kInvalidDescriptionError =
    L"Invalid type for 'description' key.";
const wchar_t* Extension::kInvalidContentScriptsListError =
    L"Invalid type for 'content_scripts' key.";
const wchar_t* Extension::kInvalidContentScriptError =
    L"Invalid type for content_scripts at index ";

bool Extension::InitFromValue(const DictionaryValue& source,
                              std::wstring* error) {
  // Check format version.
  int format_version = 0;
  if (!source.GetInteger(kFormatVersionKey, &format_version) ||
      format_version != kExpectedFormatVersion) {
    *error = kInvalidFormatVersionError;
    return false;
  }

  // Initialize id.
  if (!source.GetString(kIdKey, &id_)) {
    *error = kInvalidIdError;
    return false;
  }

  // Initialize name.
  if (!source.GetString(kNameKey, &name_)) {
    *error = kInvalidNameError;
    return false;
  }

  // Initialize description (optional).
  Value* value = NULL;
  if (source.Get(kDescriptionKey, &value)) {
    if (!value->GetAsString(&description_)) {
      *error = kInvalidDescriptionError;
      return false;
    }
  }

  // Initialize content scripts (optional).
  if (source.Get(kContentScriptsKey, &value)) {
    ListValue* list_value = NULL;
    if (value->GetType() != Value::TYPE_LIST) {
      *error = kInvalidContentScriptsListError;
      return false;
    } else {
      list_value = static_cast<ListValue*>(value);
    }

    for (size_t i = 0; i < list_value->GetSize(); ++i) {
      std::wstring content_script;
      if (!list_value->Get(i, &value) || !value->GetAsString(&content_script)) {
        *error = kInvalidContentScriptError;
        *error += IntToWString(i);
        return false;
      }

      content_scripts_.push_back(content_script);
    }
  }

  return true;
}

void Extension::CopyToValue(DictionaryValue* destination) {
  // Set format version
  destination->SetInteger(kFormatVersionKey,
                          kExpectedFormatVersion);

  // Copy id.
  destination->SetString(kIdKey, id_);

  // Copy name.
  destination->SetString(kNameKey, name_);

  // Copy description (optional).
  if (description_.size() > 0)
    destination->SetString(kDescriptionKey, description_);

  // Copy content scripts (optional).
  if (content_scripts_.size() > 0) {
    ListValue* list_value = new ListValue();
    destination->Set(kContentScriptsKey, list_value);

    for (size_t i = 0; i < content_scripts_.size(); ++i) {
      list_value->Set(i, Value::CreateStringValue(content_scripts_[i]));
    }
  }
}
<|endoftext|>


<commit_before><commit_msg>Add a const cast.<commit_after><|endoftext|>


<commit_before><commit_msg>Fixes regression in placement of new tab button.<commit_after><|endoftext|>