<commit_before>#include "chainerx/cuda/memory_pool.h"

#include <algorithm>
#include <iterator>
#include <mutex>
#include <unordered_map>
#include <vector>

#include "chainerx/cuda/cuda_runtime.h"
#include "chainerx/cuda/cuda_set_device_scope.h"
#include "chainerx/macro.h"

namespace chainerx {
namespace cuda {

MallocStatus DeviceMemoryAllocator::Malloc(void** ptr, size_t bytesize) {
    cudaError_t status = cudaMallocManaged(ptr, bytesize, cudaMemAttachGlobal);
    switch (status) {
        case cudaSuccess:
            return MallocStatus::kSuccess;
        case cudaErrorMemoryAllocation:
            return MallocStatus::kErrorMemoryAllocation;
        default:
            Throw(status);
    }
    CHAINERX_NEVER_REACH();
}

MallocStatus PinnedMemoryAllocator::Malloc(void** ptr, size_t bytesize) {
    cudaError_t status = cudaHostAlloc(ptr, bytesize, cudaHostAllocWriteCombined);
    switch (status) {
        case cudaSuccess:
            return MallocStatus::kSuccess;
        case cudaErrorMemoryAllocation:
            return MallocStatus::kErrorMemoryAllocation;
        default:
            Throw(status);
    }
    CHAINERX_NEVER_REACH();
}

std::unique_ptr<Chunk> Chunk::Split(size_t bytesize) {
    CHAINERX_ASSERT(bytesize_ >= bytesize);
    if (bytesize_ == bytesize) {
        return nullptr;
    }

    std::unique_ptr<Chunk> remaining = std::make_unique<Chunk>(ptr(), bytesize, bytesize_ - bytesize);
    bytesize_ = bytesize;

    if (next_ != nullptr) {
        remaining->SetNext(next_);
        remaining->next()->SetPrev(remaining.get());
    }
    next_ = remaining.get();
    remaining->SetPrev(this);

    return remaining;
}

void Chunk::MergeWithNext() {
    CHAINERX_ASSERT(next_ != nullptr);
    bytesize_ += next_->bytesize();
    if (next_->next() != nullptr) {
        next_->next()->SetPrev(this);
    }
    next_ = next_->next();
}

// Pushes a chunk into an appropriate free list
//
// Not thread-safe
void MemoryPool::PushIntoFreeList(std::unique_ptr<Chunk> chunk) {
    FreeList& free_list = free_bins_[chunk->bytesize()];
    free_list.emplace_back(std::move(chunk));
}

void MemoryPool::CompactFreeBins(std::map<size_t, FreeList>::iterator it_start, std::map<size_t, FreeList>::iterator it_end) {
    auto it_start_rev = std::make_reverse_iterator(it_start);
    it_start = std::find_if(it_start_rev, free_bins_.rend(), [](const auto& p) { return !p.second.empty(); }).base();
    it_end = std::find_if(it_end, free_bins_.end(), [](const auto& p) { return !p.second.empty(); });
    free_bins_.erase(it_start, it_end);
}

// Finds best-fit, or a smallest larger allocation if available
//
// Not thread-safe
std::unique_ptr<Chunk> MemoryPool::PopFromFreeList(size_t allocation_size) {
    auto it_start = free_bins_.lower_bound(allocation_size);
    size_t distance{0};
    for (auto it = it_start; it != free_bins_.end(); ++it, ++distance) {
        FreeList& free_list = it->second;
        if (free_list.empty()) {
            continue;
        }
        std::unique_ptr<Chunk> chunk = std::move(free_list.back());
        CHAINERX_ASSERT(chunk != nullptr);
        free_list.pop_back();
        if (distance > kCompactionThreashold) {
            CompactFreeBins(it_start, it);
        }
        return chunk;
    }

    return nullptr;
}

// Removes a chunk from an appropriate free list, and returns the removed chunk
//
// Not thread-safe
std::unique_ptr<Chunk> MemoryPool::RemoveChunkFromFreeList(Chunk* chunk) {
    CHAINERX_ASSERT(chunk != nullptr);

    // Find an appropriate free list
    auto free_bins_it = free_bins_.find(chunk->bytesize());
    if (free_bins_it == free_bins_.end()) {
        return nullptr;
    }
    FreeList& free_list = free_bins_it->second;

    // Remove the given chunk from the found free list
    auto it = std::find_if(free_list.begin(), free_list.end(), [chunk](const auto& ptr) { return ptr.get() == chunk; });
    if (it == free_list.end()) return nullptr;
    std::unique_ptr<Chunk> chunk_uniq_ptr = std::move(*it);
    CHAINERX_ASSERT(chunk_uniq_ptr != nullptr);
    free_list.erase(it);
    return chunk_uniq_ptr;
}

MemoryPool::~MemoryPool() {
    // NOTE: CudaSetDeviceScope is not available at dtor because it may throw
    int orig_device_index{0};
    cudaGetDevice(&orig_device_index);
    cudaSetDevice(device_index_);

    for (auto& item : free_bins_) {
        FreeList& free_list = item.second;
        for (const std::unique_ptr<Chunk>& chunk : free_list) {
            if (chunk->prev() == nullptr) {
                allocator_->Free(chunk->ptr());
            }
        }
    }
    // Ideally, in_use_ should be empty, but it could happen that shared ptrs to memories allocated
    // by this memory pool are released after this memory pool is destructed.
    // Our approach is that we anyway free CUDA memories held by this memory pool here in such case.
    // Operators of arrays holding such memories will be broken, but are not supported.
    for (const auto& item : in_use_) {
        const std::unique_ptr<Chunk>& chunk = item.second;
        if (chunk->prev() == nullptr) {
            allocator_->Free(chunk->ptr());
        }
    }

    cudaSetDevice(orig_device_index);
}

void MemoryPool::FreeUnusedBlocks() {
    CudaSetDeviceScope scope{device_index_};

    for (auto free_bins_it = free_bins_.begin(); free_bins_it != free_bins_.end();) {
        FreeList& free_list = free_bins_it->second;
        for (auto free_list_it = free_list.begin(); free_list_it != free_list.end();) {
            std::unique_ptr<Chunk>& chunk = *free_list_it;
            if (chunk->next() == nullptr && chunk->prev() == nullptr) {
                allocator_->Free(chunk->ptr());
                free_list_it = free_list.erase(free_list_it);
            } else {
                ++free_list_it;
            }
        }
        // Compact free_bins
        if (free_list.empty()) {
            free_bins_it = free_bins_.erase(free_bins_it);
        } else {
            ++free_bins_it;
        }
    }
    if (free_bins_.empty()) {
        free_bins_.clear();
    }
}

void* MemoryPool::Malloc(size_t bytesize) {
    if (bytesize == 0) {
        return nullptr;
    }

    // TODO(niboshi): Currently the deleter of allocated memory assumes that
    // the memory is stored in the memory pool (in `in_use_`), but this may not hold if some exception is thrown before it is stored.
    // `std::lock_guard` and `in_use_.emplace` are the sources of possible exceptions.

    size_t allocation_size = GetAllocationSize(bytesize);
    std::unique_ptr<Chunk> chunk{nullptr};

    {
        std::lock_guard<std::mutex> lock{free_bins_mutex_};
        chunk = PopFromFreeList(allocation_size);
    }

    if (chunk != nullptr) {
        std::unique_ptr<Chunk> remaining = chunk->Split(allocation_size);
        if (remaining != nullptr) {
            std::lock_guard<std::mutex> lock{free_bins_mutex_};
            PushIntoFreeList(std::move(remaining));
        }
    } else {
        void* ptr{nullptr};
        CudaSetDeviceScope scope{device_index_};
        MallocStatus status = allocator_->Malloc(&ptr, allocation_size);
        if (status == MallocStatus::kErrorMemoryAllocation) {
            FreeUnusedBlocks();
            status = allocator_->Malloc(&ptr, allocation_size);
            if (status == MallocStatus::kErrorMemoryAllocation) {
                // TODO(sonots): Include total pooled bytes in the error message
                throw OutOfMemoryError{allocation_size};
            }
        }
        chunk = std::make_unique<Chunk>(ptr, 0, allocation_size);
    }

    CHAINERX_ASSERT(chunk != nullptr);
    void* ptr = chunk->ptr();
    {
        std::lock_guard<std::mutex> lock{in_use_mutex_};
        in_use_.emplace(ptr, std::move(chunk));
    }
    return ptr;
}

void MemoryPool::Free(void* ptr) {
    if (ptr == nullptr) {
        return;
    }
    std::unique_ptr<Chunk> chunk{nullptr};

    {
        std::lock_guard<std::mutex> lock{in_use_mutex_};
        auto it = in_use_.find(ptr);
        if (it == in_use_.end()) {
            throw ChainerxError{"Cannot free out-of-pool memory"};
        }
        chunk = std::move(it->second);
        in_use_.erase(it);
    }

    CHAINERX_ASSERT(chunk != nullptr);
    {
        std::lock_guard<std::mutex> lock{free_bins_mutex_};

        // If there are free chunks before this chunk, merges it with them.
        if (chunk->next() != nullptr) {
            std::unique_ptr<Chunk> chunk_next = RemoveChunkFromFreeList(chunk->next());
            if (chunk_next != nullptr) {
                chunk->MergeWithNext();
            }
        }

        // If there are free chunks after this chunk, merges it with them.
        if (chunk->prev() != nullptr) {
            std::unique_ptr<Chunk> chunk_prev = RemoveChunkFromFreeList(chunk->prev());
            if (chunk_prev != nullptr) {
                chunk_prev->MergeWithNext();
                chunk = std::move(chunk_prev);
            }
        }

        // Pushes this chunk into the free list.
        PushIntoFreeList(std::move(chunk));
    }
}

void MemoryPool::FreeNoExcept(void* ptr) noexcept {
    try {
        Free(ptr);
    } catch (...) {
        CHAINERX_NEVER_REACH();
    }
}

}  // namespace cuda
}  // namespace chainerx
<commit_msg>Enhanced algorithm of FreeUnusedBlocks<commit_after>#include "chainerx/cuda/memory_pool.h"

#include <algorithm>
#include <iterator>
#include <mutex>
#include <unordered_map>
#include <vector>

#include "chainerx/cuda/cuda_runtime.h"
#include "chainerx/cuda/cuda_set_device_scope.h"
#include "chainerx/macro.h"

namespace chainerx {
namespace cuda {

MallocStatus DeviceMemoryAllocator::Malloc(void** ptr, size_t bytesize) {
    cudaError_t status = cudaMallocManaged(ptr, bytesize, cudaMemAttachGlobal);
    switch (status) {
        case cudaSuccess:
            return MallocStatus::kSuccess;
        case cudaErrorMemoryAllocation:
            return MallocStatus::kErrorMemoryAllocation;
        default:
            Throw(status);
    }
    CHAINERX_NEVER_REACH();
}

MallocStatus PinnedMemoryAllocator::Malloc(void** ptr, size_t bytesize) {
    cudaError_t status = cudaHostAlloc(ptr, bytesize, cudaHostAllocWriteCombined);
    switch (status) {
        case cudaSuccess:
            return MallocStatus::kSuccess;
        case cudaErrorMemoryAllocation:
            return MallocStatus::kErrorMemoryAllocation;
        default:
            Throw(status);
    }
    CHAINERX_NEVER_REACH();
}

std::unique_ptr<Chunk> Chunk::Split(size_t bytesize) {
    CHAINERX_ASSERT(bytesize_ >= bytesize);
    if (bytesize_ == bytesize) {
        return nullptr;
    }

    std::unique_ptr<Chunk> remaining = std::make_unique<Chunk>(ptr(), bytesize, bytesize_ - bytesize);
    bytesize_ = bytesize;

    if (next_ != nullptr) {
        remaining->SetNext(next_);
        remaining->next()->SetPrev(remaining.get());
    }
    next_ = remaining.get();
    remaining->SetPrev(this);

    return remaining;
}

void Chunk::MergeWithNext() {
    CHAINERX_ASSERT(next_ != nullptr);
    bytesize_ += next_->bytesize();
    if (next_->next() != nullptr) {
        next_->next()->SetPrev(this);
    }
    next_ = next_->next();
}

// Pushes a chunk into an appropriate free list
//
// Not thread-safe
void MemoryPool::PushIntoFreeList(std::unique_ptr<Chunk> chunk) {
    FreeList& free_list = free_bins_[chunk->bytesize()];
    free_list.emplace_back(std::move(chunk));
}

void MemoryPool::CompactFreeBins(std::map<size_t, FreeList>::iterator it_start, std::map<size_t, FreeList>::iterator it_end) {
    auto it_start_rev = std::make_reverse_iterator(it_start);
    it_start = std::find_if(it_start_rev, free_bins_.rend(), [](const auto& p) { return !p.second.empty(); }).base();
    it_end = std::find_if(it_end, free_bins_.end(), [](const auto& p) { return !p.second.empty(); });
    free_bins_.erase(it_start, it_end);
}

// Finds best-fit, or a smallest larger allocation if available
//
// Not thread-safe
std::unique_ptr<Chunk> MemoryPool::PopFromFreeList(size_t allocation_size) {
    auto it_start = free_bins_.lower_bound(allocation_size);
    size_t distance{0};
    for (auto it = it_start; it != free_bins_.end(); ++it, ++distance) {
        FreeList& free_list = it->second;
        if (free_list.empty()) {
            continue;
        }
        std::unique_ptr<Chunk> chunk = std::move(free_list.back());
        CHAINERX_ASSERT(chunk != nullptr);
        free_list.pop_back();
        if (distance > kCompactionThreashold) {
            CompactFreeBins(it_start, it);
        }
        return chunk;
    }

    return nullptr;
}

// Removes a chunk from an appropriate free list, and returns the removed chunk
//
// Not thread-safe
std::unique_ptr<Chunk> MemoryPool::RemoveChunkFromFreeList(Chunk* chunk) {
    CHAINERX_ASSERT(chunk != nullptr);

    // Find an appropriate free list
    auto free_bins_it = free_bins_.find(chunk->bytesize());
    if (free_bins_it == free_bins_.end()) {
        return nullptr;
    }
    FreeList& free_list = free_bins_it->second;

    // Remove the given chunk from the found free list
    auto it = std::find_if(free_list.begin(), free_list.end(), [chunk](const auto& ptr) { return ptr.get() == chunk; });
    if (it == free_list.end()) return nullptr;
    std::unique_ptr<Chunk> chunk_uniq_ptr = std::move(*it);
    CHAINERX_ASSERT(chunk_uniq_ptr != nullptr);
    free_list.erase(it);
    return chunk_uniq_ptr;
}

MemoryPool::~MemoryPool() {
    // NOTE: CudaSetDeviceScope is not available at dtor because it may throw
    int orig_device_index{0};
    cudaGetDevice(&orig_device_index);
    cudaSetDevice(device_index_);

    for (auto& item : free_bins_) {
        FreeList& free_list = item.second;
        for (const std::unique_ptr<Chunk>& chunk : free_list) {
            if (chunk->prev() == nullptr) {
                allocator_->Free(chunk->ptr());
            }
        }
    }
    // Ideally, in_use_ should be empty, but it could happen that shared ptrs to memories allocated
    // by this memory pool are released after this memory pool is destructed.
    // Our approach is that we anyway free CUDA memories held by this memory pool here in such case.
    // Operators of arrays holding such memories will be broken, but are not supported.
    for (const auto& item : in_use_) {
        const std::unique_ptr<Chunk>& chunk = item.second;
        if (chunk->prev() == nullptr) {
            allocator_->Free(chunk->ptr());
        }
    }

    cudaSetDevice(orig_device_index);
}

void MemoryPool::FreeUnusedBlocks() {
    CudaSetDeviceScope scope{device_index_};
    for (auto free_bins_it = free_bins_.begin(); free_bins_it != free_bins_.end();) {
        FreeList new_free_list;
        for (auto& chunk : free_bins_it->second) {
            if (chunk->next() == nullptr && chunk->prev() == nullptr) {
                allocator_->Free(chunk->ptr());
            } else {
                new_free_list.emplace_back(std::move(chunk));
            }
        }
        if (!new_free_list.empty()) {
            free_bins_it->second = std::move(new_free_list);
            free_bins_it++;
        } else {
            free_bins_it = free_bins_.erase(free_bins_it);
        }
    }
}

void* MemoryPool::Malloc(size_t bytesize) {
    if (bytesize == 0) {
        return nullptr;
    }

    // TODO(niboshi): Currently the deleter of allocated memory assumes that
    // the memory is stored in the memory pool (in `in_use_`), but this may not hold if some exception is thrown before it is stored.
    // `std::lock_guard` and `in_use_.emplace` are the sources of possible exceptions.

    size_t allocation_size = GetAllocationSize(bytesize);
    std::unique_ptr<Chunk> chunk{nullptr};

    {
        std::lock_guard<std::mutex> lock{free_bins_mutex_};
        chunk = PopFromFreeList(allocation_size);
    }

    if (chunk != nullptr) {
        std::unique_ptr<Chunk> remaining = chunk->Split(allocation_size);
        if (remaining != nullptr) {
            std::lock_guard<std::mutex> lock{free_bins_mutex_};
            PushIntoFreeList(std::move(remaining));
        }
    } else {
        void* ptr{nullptr};
        CudaSetDeviceScope scope{device_index_};
        MallocStatus status = allocator_->Malloc(&ptr, allocation_size);
        if (status == MallocStatus::kErrorMemoryAllocation) {
            FreeUnusedBlocks();
            status = allocator_->Malloc(&ptr, allocation_size);
            if (status == MallocStatus::kErrorMemoryAllocation) {
                // TODO(sonots): Include total pooled bytes in the error message
                throw OutOfMemoryError{allocation_size};
            }
        }
        chunk = std::make_unique<Chunk>(ptr, 0, allocation_size);
    }

    CHAINERX_ASSERT(chunk != nullptr);
    void* ptr = chunk->ptr();
    {
        std::lock_guard<std::mutex> lock{in_use_mutex_};
        in_use_.emplace(ptr, std::move(chunk));
    }
    return ptr;
}

void MemoryPool::Free(void* ptr) {
    if (ptr == nullptr) {
        return;
    }
    std::unique_ptr<Chunk> chunk{nullptr};

    {
        std::lock_guard<std::mutex> lock{in_use_mutex_};
        auto it = in_use_.find(ptr);
        if (it == in_use_.end()) {
            throw ChainerxError{"Cannot free out-of-pool memory"};
        }
        chunk = std::move(it->second);
        in_use_.erase(it);
    }

    CHAINERX_ASSERT(chunk != nullptr);
    {
        std::lock_guard<std::mutex> lock{free_bins_mutex_};

        // If there are free chunks before this chunk, merges it with them.
        if (chunk->next() != nullptr) {
            std::unique_ptr<Chunk> chunk_next = RemoveChunkFromFreeList(chunk->next());
            if (chunk_next != nullptr) {
                chunk->MergeWithNext();
            }
        }

        // If there are free chunks after this chunk, merges it with them.
        if (chunk->prev() != nullptr) {
            std::unique_ptr<Chunk> chunk_prev = RemoveChunkFromFreeList(chunk->prev());
            if (chunk_prev != nullptr) {
                chunk_prev->MergeWithNext();
                chunk = std::move(chunk_prev);
            }
        }

        // Pushes this chunk into the free list.
        PushIntoFreeList(std::move(chunk));
    }
}

void MemoryPool::FreeNoExcept(void* ptr) noexcept {
    try {
        Free(ptr);
    } catch (...) {
        CHAINERX_NEVER_REACH();
    }
}

}  // namespace cuda
}  // namespace chainerx
<|endoftext|>


<commit_before><commit_msg>Cleanup<commit_after><|endoftext|>


<commit_before><commit_msg>modif<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/external_tab_container.h"

#include "app/l10n_util.h"
#include "app/win_util.h"
#include "base/logging.h"
#include "base/win_util.h"
#include "chrome/browser/automation/automation_provider.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/load_notification_details.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/tab_contents/provisional_load_details.h"
#include "chrome/browser/views/tab_contents/render_view_context_menu_external_win.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/views/tab_contents/tab_contents_container.h"
#include "chrome/common/bindings_policy.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/notification_service.h"
#include "chrome/test/automation/automation_messages.h"

#include "grit/generated_resources.h"

static const wchar_t kWindowObjectKey[] = L"ChromeWindowObject";

// TODO(sanjeevr): The external_accel_table_ and external_accel_entry_count_
// member variables are now obsolete and we don't use them.
// We need to remove them.
ExternalTabContainer::ExternalTabContainer(
    AutomationProvider* automation)
    : automation_(automation),
      tab_contents_(NULL),
      external_accel_table_(NULL),
      external_accel_entry_count_(0),
      tab_contents_container_(NULL),
      tab_handle_(0),
      ignore_next_load_notification_(false) {
}

ExternalTabContainer::~ExternalTabContainer() {
  Uninitialize(GetNativeView());
}

bool ExternalTabContainer::Init(Profile* profile,
                                HWND parent,
                                const gfx::Rect& bounds,
                                DWORD style) {
  if (IsWindow()) {
    NOTREACHED();
    return false;
  }

  set_window_style(WS_POPUP);
  views::WidgetWin::Init(NULL, bounds);
  if (!IsWindow()) {
    NOTREACHED();
    return false;
  }
  // TODO(jcampan): limit focus traversal to contents.

  // We don't ever remove the prop because the lifetime of this object
  // is the same as the lifetime of the window
  SetProp(GetNativeView(), kWindowObjectKey, this);

  tab_contents_ = new TabContents(profile, NULL, MSG_ROUTING_NONE, NULL);
  tab_contents_->set_delegate(this);
  tab_contents_->render_view_host()->AllowBindings(
      BindingsPolicy::EXTERNAL_HOST);

  // Create a TabContentsContainer to handle focus cycling using Tab and
  // Shift-Tab.
  tab_contents_container_ = new TabContentsContainer;
  SetContentsView(tab_contents_container_);

  // Note that SetTabContents must be called after AddChildView is called
  tab_contents_container_->ChangeTabContents(tab_contents_);

  NavigationController* controller = &tab_contents_->controller();
  registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED,
                 Source<NavigationController>(controller));
  registrar_.Add(this, NotificationType::FAIL_PROVISIONAL_LOAD_WITH_ERROR,
                 Source<NavigationController>(controller));
  registrar_.Add(this, NotificationType::LOAD_STOP,
                 Source<NavigationController>(controller));
  NotificationService::current()->Notify(
      NotificationType::EXTERNAL_TAB_CREATED,
      Source<NavigationController>(controller),
      NotificationService::NoDetails());

  // We need WS_POPUP to be on the window during initialization, but
  // once initialized we apply the requested style which may or may not
  // include the popup bit.
  // Note that it's important to do this before we call SetParent since
  // during the SetParent call we will otherwise get a WA_ACTIVATE call
  // that causes us to steal the current focus.
  SetWindowLong(GWL_STYLE, (GetWindowLong(GWL_STYLE) & ~WS_POPUP) | style);

  // Now apply the parenting and style
  if (parent)
    SetParent(GetNativeView(), parent);

  ::ShowWindow(tab_contents_->GetNativeView(), SW_SHOWNA);

  disabled_context_menu_ids_.push_back(
      IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD);
  return true;
}

void ExternalTabContainer::SetAccelerators(HACCEL accel_table,
                                           int accel_table_entry_count) {
  external_accel_table_ = accel_table;
  external_accel_entry_count_ = accel_table_entry_count;
}

void ExternalTabContainer::ProcessUnhandledAccelerator(const MSG& msg) {
  // We just received an accelerator key that we had sent to external host
  // back. Since the external host was not interested in handling this, we
  // need to dispatch this message as if we had just peeked this out. (we
  // also need to call TranslateMessage to generate a WM_CHAR if needed).
  TranslateMessage(&msg);
  DispatchMessage(&msg);
}

void ExternalTabContainer::FocusThroughTabTraversal(bool reverse) {
  DCHECK(tab_contents_);
  if (tab_contents_) {
    static_cast<TabContents*>(tab_contents_)->Focus();
    static_cast<TabContents*>(tab_contents_)->FocusThroughTabTraversal(reverse);
  }
}

// static
bool ExternalTabContainer::IsExternalTabContainer(HWND window) {
  if (GetProp(window, kWindowObjectKey) != NULL)
    return true;

  return false;
}

// static
ExternalTabContainer* ExternalTabContainer::GetContainerForTab(
    HWND tab_window) {
  HWND parent_window = ::GetParent(tab_window);
  if (!::IsWindow(parent_window)) {
    return NULL;
  }
  if (!IsExternalTabContainer(parent_window)) {
    return NULL;
  }
  ExternalTabContainer* container = reinterpret_cast<ExternalTabContainer*>(
      GetProp(parent_window, kWindowObjectKey));
  return container;
}

////////////////////////////////////////////////////////////////////////////////
// ExternalTabContainer, TabContentsDelegate implementation:

void ExternalTabContainer::OpenURLFromTab(TabContents* source,
                           const GURL& url,
                           const GURL& referrer,
                           WindowOpenDisposition disposition,
                           PageTransition::Type transition) {
  switch (disposition) {
    case CURRENT_TAB:
    case SINGLETON_TAB:
    case NEW_FOREGROUND_TAB:
    case NEW_BACKGROUND_TAB:
    case NEW_WINDOW:
      if (automation_) {
        automation_->Send(new AutomationMsg_OpenURL(0, tab_handle_,
                                                    url, disposition));
      }
      break;
    default:
      break;
  }
}

void ExternalTabContainer::NavigationStateChanged(const TabContents* source,
                                                  unsigned changed_flags) {
  if (automation_) {
    automation_->Send(new AutomationMsg_NavigationStateChanged(0, tab_handle_,
                                                               changed_flags));
  }
}

void ExternalTabContainer::AddNewContents(TabContents* source,
                            TabContents* new_contents,
                            WindowOpenDisposition disposition,
                            const gfx::Rect& initial_pos,
                            bool user_gesture) {
  if (disposition == NEW_POPUP || disposition == NEW_WINDOW) {
    Browser::BuildPopupWindowHelper(source, new_contents, initial_pos,
                                    Browser::TYPE_POPUP,
                                    tab_contents_->profile(), true);
  } else {
    NOTREACHED();
  }
}

void ExternalTabContainer::ActivateContents(TabContents* contents) {
}

void ExternalTabContainer::LoadingStateChanged(TabContents* source) {
}

void ExternalTabContainer::CloseContents(TabContents* source) {
}

void ExternalTabContainer::MoveContents(TabContents* source,
                                        const gfx::Rect& pos) {
}

bool ExternalTabContainer::IsPopup(TabContents* source) {
  return false;
}

void ExternalTabContainer::URLStarredChanged(TabContents* source,
                                             bool starred) {
}

void ExternalTabContainer::UpdateTargetURL(TabContents* source,
                                           const GURL& url) {
  if (automation_) {
    std::wstring url_string = CA2W(url.spec().c_str());
    automation_->Send(
        new AutomationMsg_UpdateTargetUrl(0, tab_handle_, url_string));
  }
}

void ExternalTabContainer::ContentsZoomChange(bool zoom_in) {
}

void ExternalTabContainer::ToolbarSizeChanged(TabContents* source,
                                              bool finished) {
}

void ExternalTabContainer::ForwardMessageToExternalHost(
    const std::string& message, const std::string& origin,
    const std::string& target) {
  if(automation_) {
    automation_->Send(
        new AutomationMsg_ForwardMessageToExternalHost(0, tab_handle_,
            message, origin, target));
  }
}

bool ExternalTabContainer::TakeFocus(bool reverse) {
  if (automation_) {
    automation_->Send(new AutomationMsg_TabbedOut(0, tab_handle_,
        win_util::IsShiftPressed()));
  }

  return true;
}

bool ExternalTabContainer::HandleContextMenu(const ContextMenuParams& params) {
  if (!automation_) {
    NOTREACHED();
    return false;
  }

  external_context_menu_.reset(
      new RenderViewContextMenuExternalWin(tab_contents(),
                                           params,
                                           disabled_context_menu_ids_));
  external_context_menu_->Init();

  POINT screen_pt = { params.x, params.y };
  MapWindowPoints(GetNativeView(), HWND_DESKTOP, &screen_pt, 1);

  bool rtl = l10n_util::TextDirection() == l10n_util::RIGHT_TO_LEFT;
  automation_->Send(
      new AutomationMsg_ForwardContextMenuToExternalHost(0, tab_handle_,
          external_context_menu_->GetMenuHandle(), screen_pt.x, screen_pt.y,
          rtl ? TPM_RIGHTALIGN : TPM_LEFTALIGN));

  return true;
}

bool ExternalTabContainer::ExecuteContextMenuCommand(int command) {
  if (!external_context_menu_.get()) {
    NOTREACHED();
    return false;
  }

  external_context_menu_->ExecuteCommand(command);
  return true;
}

////////////////////////////////////////////////////////////////////////////////
// ExternalTabContainer, NotificationObserver implementation:

void ExternalTabContainer::Observe(NotificationType type,
                                   const NotificationSource& source,
                                   const NotificationDetails& details) {
  if (!automation_)
    return;

  static const int kHttpClientErrorStart = 400;
  static const int kHttpServerErrorEnd = 510;

  switch (type.value) {
    case NotificationType::LOAD_STOP: {
        const LoadNotificationDetails* load =
            Details<LoadNotificationDetails>(details).ptr();
        if (load != NULL && PageTransition::IsMainFrame(load->origin())) {
          automation_->Send(new AutomationMsg_TabLoaded(0, tab_handle_,
                                                        load->url()));
        }
        break;
      }
    case NotificationType::NAV_ENTRY_COMMITTED: {
        if (ignore_next_load_notification_) {
          ignore_next_load_notification_ = false;
          return;
        }

        const NavigationController::LoadCommittedDetails* commit =
            Details<NavigationController::LoadCommittedDetails>(details).ptr();

        if (commit->http_status_code >= kHttpClientErrorStart &&
            commit->http_status_code <= kHttpServerErrorEnd) {
          automation_->Send(new AutomationMsg_NavigationFailed(
              0, tab_handle_, commit->http_status_code, commit->entry->url()));

          ignore_next_load_notification_ = true;
        } else {
          // When the previous entry index is invalid, it will be -1, which
          // will still make the computation come out right (navigating to the
          // 0th entry will be +1).
          automation_->Send(new AutomationMsg_DidNavigate(
              0, tab_handle_, commit->type,
              commit->previous_entry_index -
                  tab_contents_->controller().last_committed_entry_index(),
              commit->entry->url()));
        }
        break;
      }
    case NotificationType::FAIL_PROVISIONAL_LOAD_WITH_ERROR: {
      const ProvisionalLoadDetails* load_details =
          Details<ProvisionalLoadDetails>(details).ptr();
      automation_->Send(new AutomationMsg_NavigationFailed(
          0, tab_handle_, load_details->error_code(), load_details->url()));

      ignore_next_load_notification_ = true;
      break;
    }
    default:
      NOTREACHED();
  }
}

////////////////////////////////////////////////////////////////////////////////
// ExternalTabContainer, views::WidgetWin overrides:

void ExternalTabContainer::OnDestroy() {
  Uninitialize(GetNativeView());
  WidgetWin::OnDestroy();
}

////////////////////////////////////////////////////////////////////////////////
// ExternalTabContainer, views::KeystrokeListener implementation:

bool ExternalTabContainer::ProcessKeyStroke(HWND window, UINT message,
                                            WPARAM wparam, LPARAM lparam) {
  if (!automation_) {
    return false;
  }
  if ((wparam == VK_TAB) && !win_util::IsCtrlPressed()) {
    // Tabs are handled separately (except if this is Ctrl-Tab or
    // Ctrl-Shift-Tab)
    return false;
  }

  unsigned int flags = HIWORD(lparam);
  bool alt = (flags & KF_ALTDOWN) != 0;
  if (!alt && (message == WM_SYSKEYUP || message == WM_KEYUP)) {
    // In case the Alt key is being released.
    alt = (wparam == VK_MENU);
  }

  if ((flags & KF_EXTENDED) || alt || (wparam >= VK_F1 && wparam <= VK_F24) ||
      wparam == VK_ESCAPE || wparam == VK_RETURN ||
      win_util::IsShiftPressed() || win_util::IsCtrlPressed()) {
    // If this is an extended key or if one or more of Alt, Shift and Control
    // are pressed, this might be an accelerator that the external host wants
    // to handle. If the host does not handle this accelerator, it will reflect
    // the accelerator back to us via the ProcessUnhandledAccelerator method.
    MSG msg = {0};
    msg.hwnd = window;
    msg.message = message;
    msg.wParam = wparam;
    msg.lParam = lparam;
    automation_->Send(new AutomationMsg_HandleAccelerator(0, tab_handle_, msg));
    return true;
  }

  return false;
}

////////////////////////////////////////////////////////////////////////////////
// ExternalTabContainer, private:

void ExternalTabContainer::Uninitialize(HWND window) {
  if (tab_contents_) {
    NotificationService::current()->Notify(
        NotificationType::EXTERNAL_TAB_CLOSED,
        Source<NavigationController>(&tab_contents_->controller()),
        Details<ExternalTabContainer>(this));

    delete tab_contents_;
    tab_contents_ = NULL;
  }
}
<commit_msg>Fix a typo.  TextDirection is the name of the enum, while GetTextDirection returns the current text direction.  Also fixes a few lint nits.<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/external_tab_container.h"

#include <string>

#include "app/l10n_util.h"
#include "app/win_util.h"
#include "base/logging.h"
#include "base/win_util.h"
#include "chrome/browser/automation/automation_provider.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/load_notification_details.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/tab_contents/provisional_load_details.h"
#include "chrome/browser/views/tab_contents/render_view_context_menu_external_win.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/views/tab_contents/tab_contents_container.h"
#include "chrome/common/bindings_policy.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/notification_service.h"
#include "chrome/test/automation/automation_messages.h"

#include "grit/generated_resources.h"

static const wchar_t kWindowObjectKey[] = L"ChromeWindowObject";

// TODO(sanjeevr): The external_accel_table_ and external_accel_entry_count_
// member variables are now obsolete and we don't use them.
// We need to remove them.
ExternalTabContainer::ExternalTabContainer(
    AutomationProvider* automation)
    : automation_(automation),
      tab_contents_(NULL),
      external_accel_table_(NULL),
      external_accel_entry_count_(0),
      tab_contents_container_(NULL),
      tab_handle_(0),
      ignore_next_load_notification_(false) {
}

ExternalTabContainer::~ExternalTabContainer() {
  Uninitialize(GetNativeView());
}

bool ExternalTabContainer::Init(Profile* profile,
                                HWND parent,
                                const gfx::Rect& bounds,
                                DWORD style) {
  if (IsWindow()) {
    NOTREACHED();
    return false;
  }

  set_window_style(WS_POPUP);
  views::WidgetWin::Init(NULL, bounds);
  if (!IsWindow()) {
    NOTREACHED();
    return false;
  }
  // TODO(jcampan): limit focus traversal to contents.

  // We don't ever remove the prop because the lifetime of this object
  // is the same as the lifetime of the window
  SetProp(GetNativeView(), kWindowObjectKey, this);

  tab_contents_ = new TabContents(profile, NULL, MSG_ROUTING_NONE, NULL);
  tab_contents_->set_delegate(this);
  tab_contents_->render_view_host()->AllowBindings(
      BindingsPolicy::EXTERNAL_HOST);

  // Create a TabContentsContainer to handle focus cycling using Tab and
  // Shift-Tab.
  tab_contents_container_ = new TabContentsContainer;
  SetContentsView(tab_contents_container_);

  // Note that SetTabContents must be called after AddChildView is called
  tab_contents_container_->ChangeTabContents(tab_contents_);

  NavigationController* controller = &tab_contents_->controller();
  registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED,
                 Source<NavigationController>(controller));
  registrar_.Add(this, NotificationType::FAIL_PROVISIONAL_LOAD_WITH_ERROR,
                 Source<NavigationController>(controller));
  registrar_.Add(this, NotificationType::LOAD_STOP,
                 Source<NavigationController>(controller));
  NotificationService::current()->Notify(
      NotificationType::EXTERNAL_TAB_CREATED,
      Source<NavigationController>(controller),
      NotificationService::NoDetails());

  // We need WS_POPUP to be on the window during initialization, but
  // once initialized we apply the requested style which may or may not
  // include the popup bit.
  // Note that it's important to do this before we call SetParent since
  // during the SetParent call we will otherwise get a WA_ACTIVATE call
  // that causes us to steal the current focus.
  SetWindowLong(GWL_STYLE, (GetWindowLong(GWL_STYLE) & ~WS_POPUP) | style);

  // Now apply the parenting and style
  if (parent)
    SetParent(GetNativeView(), parent);

  ::ShowWindow(tab_contents_->GetNativeView(), SW_SHOWNA);

  disabled_context_menu_ids_.push_back(
      IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD);
  return true;
}

void ExternalTabContainer::SetAccelerators(HACCEL accel_table,
                                           int accel_table_entry_count) {
  external_accel_table_ = accel_table;
  external_accel_entry_count_ = accel_table_entry_count;
}

void ExternalTabContainer::ProcessUnhandledAccelerator(const MSG& msg) {
  // We just received an accelerator key that we had sent to external host
  // back. Since the external host was not interested in handling this, we
  // need to dispatch this message as if we had just peeked this out. (we
  // also need to call TranslateMessage to generate a WM_CHAR if needed).
  TranslateMessage(&msg);
  DispatchMessage(&msg);
}

void ExternalTabContainer::FocusThroughTabTraversal(bool reverse) {
  DCHECK(tab_contents_);
  if (tab_contents_) {
    static_cast<TabContents*>(tab_contents_)->Focus();
    static_cast<TabContents*>(tab_contents_)->FocusThroughTabTraversal(reverse);
  }
}

// static
bool ExternalTabContainer::IsExternalTabContainer(HWND window) {
  if (GetProp(window, kWindowObjectKey) != NULL)
    return true;

  return false;
}

// static
ExternalTabContainer* ExternalTabContainer::GetContainerForTab(
    HWND tab_window) {
  HWND parent_window = ::GetParent(tab_window);
  if (!::IsWindow(parent_window)) {
    return NULL;
  }
  if (!IsExternalTabContainer(parent_window)) {
    return NULL;
  }
  ExternalTabContainer* container = reinterpret_cast<ExternalTabContainer*>(
      GetProp(parent_window, kWindowObjectKey));
  return container;
}

////////////////////////////////////////////////////////////////////////////////
// ExternalTabContainer, TabContentsDelegate implementation:

void ExternalTabContainer::OpenURLFromTab(TabContents* source,
                           const GURL& url,
                           const GURL& referrer,
                           WindowOpenDisposition disposition,
                           PageTransition::Type transition) {
  switch (disposition) {
    case CURRENT_TAB:
    case SINGLETON_TAB:
    case NEW_FOREGROUND_TAB:
    case NEW_BACKGROUND_TAB:
    case NEW_WINDOW:
      if (automation_) {
        automation_->Send(new AutomationMsg_OpenURL(0, tab_handle_,
                                                    url, disposition));
      }
      break;
    default:
      break;
  }
}

void ExternalTabContainer::NavigationStateChanged(const TabContents* source,
                                                  unsigned changed_flags) {
  if (automation_) {
    automation_->Send(new AutomationMsg_NavigationStateChanged(0, tab_handle_,
                                                               changed_flags));
  }
}

void ExternalTabContainer::AddNewContents(TabContents* source,
                            TabContents* new_contents,
                            WindowOpenDisposition disposition,
                            const gfx::Rect& initial_pos,
                            bool user_gesture) {
  if (disposition == NEW_POPUP || disposition == NEW_WINDOW) {
    Browser::BuildPopupWindowHelper(source, new_contents, initial_pos,
                                    Browser::TYPE_POPUP,
                                    tab_contents_->profile(), true);
  } else {
    NOTREACHED();
  }
}

void ExternalTabContainer::ActivateContents(TabContents* contents) {
}

void ExternalTabContainer::LoadingStateChanged(TabContents* source) {
}

void ExternalTabContainer::CloseContents(TabContents* source) {
}

void ExternalTabContainer::MoveContents(TabContents* source,
                                        const gfx::Rect& pos) {
}

bool ExternalTabContainer::IsPopup(TabContents* source) {
  return false;
}

void ExternalTabContainer::URLStarredChanged(TabContents* source,
                                             bool starred) {
}

void ExternalTabContainer::UpdateTargetURL(TabContents* source,
                                           const GURL& url) {
  if (automation_) {
    std::wstring url_string = CA2W(url.spec().c_str());
    automation_->Send(
        new AutomationMsg_UpdateTargetUrl(0, tab_handle_, url_string));
  }
}

void ExternalTabContainer::ContentsZoomChange(bool zoom_in) {
}

void ExternalTabContainer::ToolbarSizeChanged(TabContents* source,
                                              bool finished) {
}

void ExternalTabContainer::ForwardMessageToExternalHost(
    const std::string& message, const std::string& origin,
    const std::string& target) {
  if (automation_) {
    automation_->Send(
        new AutomationMsg_ForwardMessageToExternalHost(0, tab_handle_,
            message, origin, target));
  }
}

bool ExternalTabContainer::TakeFocus(bool reverse) {
  if (automation_) {
    automation_->Send(new AutomationMsg_TabbedOut(0, tab_handle_,
        win_util::IsShiftPressed()));
  }

  return true;
}

bool ExternalTabContainer::HandleContextMenu(const ContextMenuParams& params) {
  if (!automation_) {
    NOTREACHED();
    return false;
  }

  external_context_menu_.reset(
      new RenderViewContextMenuExternalWin(tab_contents(),
                                           params,
                                           disabled_context_menu_ids_));
  external_context_menu_->Init();

  POINT screen_pt = { params.x, params.y };
  MapWindowPoints(GetNativeView(), HWND_DESKTOP, &screen_pt, 1);

  bool rtl = l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT;
  automation_->Send(
      new AutomationMsg_ForwardContextMenuToExternalHost(0, tab_handle_,
          external_context_menu_->GetMenuHandle(), screen_pt.x, screen_pt.y,
          rtl ? TPM_RIGHTALIGN : TPM_LEFTALIGN));

  return true;
}

bool ExternalTabContainer::ExecuteContextMenuCommand(int command) {
  if (!external_context_menu_.get()) {
    NOTREACHED();
    return false;
  }

  external_context_menu_->ExecuteCommand(command);
  return true;
}

////////////////////////////////////////////////////////////////////////////////
// ExternalTabContainer, NotificationObserver implementation:

void ExternalTabContainer::Observe(NotificationType type,
                                   const NotificationSource& source,
                                   const NotificationDetails& details) {
  if (!automation_)
    return;

  static const int kHttpClientErrorStart = 400;
  static const int kHttpServerErrorEnd = 510;

  switch (type.value) {
    case NotificationType::LOAD_STOP: {
        const LoadNotificationDetails* load =
            Details<LoadNotificationDetails>(details).ptr();
        if (load != NULL && PageTransition::IsMainFrame(load->origin())) {
          automation_->Send(new AutomationMsg_TabLoaded(0, tab_handle_,
                                                        load->url()));
        }
        break;
      }
    case NotificationType::NAV_ENTRY_COMMITTED: {
        if (ignore_next_load_notification_) {
          ignore_next_load_notification_ = false;
          return;
        }

        const NavigationController::LoadCommittedDetails* commit =
            Details<NavigationController::LoadCommittedDetails>(details).ptr();

        if (commit->http_status_code >= kHttpClientErrorStart &&
            commit->http_status_code <= kHttpServerErrorEnd) {
          automation_->Send(new AutomationMsg_NavigationFailed(
              0, tab_handle_, commit->http_status_code, commit->entry->url()));

          ignore_next_load_notification_ = true;
        } else {
          // When the previous entry index is invalid, it will be -1, which
          // will still make the computation come out right (navigating to the
          // 0th entry will be +1).
          automation_->Send(new AutomationMsg_DidNavigate(
              0, tab_handle_, commit->type,
              commit->previous_entry_index -
                  tab_contents_->controller().last_committed_entry_index(),
              commit->entry->url()));
        }
        break;
      }
    case NotificationType::FAIL_PROVISIONAL_LOAD_WITH_ERROR: {
      const ProvisionalLoadDetails* load_details =
          Details<ProvisionalLoadDetails>(details).ptr();
      automation_->Send(new AutomationMsg_NavigationFailed(
          0, tab_handle_, load_details->error_code(), load_details->url()));

      ignore_next_load_notification_ = true;
      break;
    }
    default:
      NOTREACHED();
  }
}

////////////////////////////////////////////////////////////////////////////////
// ExternalTabContainer, views::WidgetWin overrides:

void ExternalTabContainer::OnDestroy() {
  Uninitialize(GetNativeView());
  WidgetWin::OnDestroy();
}

////////////////////////////////////////////////////////////////////////////////
// ExternalTabContainer, views::KeystrokeListener implementation:

bool ExternalTabContainer::ProcessKeyStroke(HWND window, UINT message,
                                            WPARAM wparam, LPARAM lparam) {
  if (!automation_) {
    return false;
  }
  if ((wparam == VK_TAB) && !win_util::IsCtrlPressed()) {
    // Tabs are handled separately (except if this is Ctrl-Tab or
    // Ctrl-Shift-Tab)
    return false;
  }

  unsigned int flags = HIWORD(lparam);
  bool alt = (flags & KF_ALTDOWN) != 0;
  if (!alt && (message == WM_SYSKEYUP || message == WM_KEYUP)) {
    // In case the Alt key is being released.
    alt = (wparam == VK_MENU);
  }

  if ((flags & KF_EXTENDED) || alt || (wparam >= VK_F1 && wparam <= VK_F24) ||
      wparam == VK_ESCAPE || wparam == VK_RETURN ||
      win_util::IsShiftPressed() || win_util::IsCtrlPressed()) {
    // If this is an extended key or if one or more of Alt, Shift and Control
    // are pressed, this might be an accelerator that the external host wants
    // to handle. If the host does not handle this accelerator, it will reflect
    // the accelerator back to us via the ProcessUnhandledAccelerator method.
    MSG msg = {0};
    msg.hwnd = window;
    msg.message = message;
    msg.wParam = wparam;
    msg.lParam = lparam;
    automation_->Send(new AutomationMsg_HandleAccelerator(0, tab_handle_, msg));
    return true;
  }

  return false;
}

////////////////////////////////////////////////////////////////////////////////
// ExternalTabContainer, private:

void ExternalTabContainer::Uninitialize(HWND window) {
  if (tab_contents_) {
    NotificationService::current()->Notify(
        NotificationType::EXTERNAL_TAB_CLOSED,
        Source<NavigationController>(&tab_contents_->controller()),
        Details<ExternalTabContainer>(this));

    delete tab_contents_;
    tab_contents_ = NULL;
  }
}
<|endoftext|>


<commit_before>//
// Copyright (c) 2012, University of Erlangen-Nuremberg
// Copyright (c) 2012, Siemens AG
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//

/*
 * Tool that compiles a OpenCL kernel in order to determine the resource usage
 */

#include <algorithm>
#include <glob.h>
#include <stdio.h>
#include <unistd.h>

#include "hipacc_ocl.hpp"

void usage(char **argv) {
    fprintf(stderr, "Usage: %s [-h] [-d GPU|CPU|ALL] [-p AMD|APPLE|ARM|INTEL|NVIDIA|ALL] [-i <include_dir>] -k <kernel_name> -f <opencl_file>\n", argv[0]);
}

int main(int argc, char *argv[]) {
    int option = 0;
    cl_device_type device_type = CL_DEVICE_TYPE_ALL;
    cl_platform_name platform_name = ALL;
    std::string kernel_name;
    std::string file_name;
    std::string build_options = "";
    std::string build_includes = "";
    bool found_kernel_name = false, found_file_name = false, print_compile_progress = false, dump_binary = false, print_log = true;

    // scan command-line options
    while ((option = getopt(argc, (char * const *)argv, "hd:p:k:f:i:")) != -1) {
        switch (option) {
            case 'h':
                fprintf(stderr, "Compile OpenCL kernel for given Platform and/or device type.\n");
                usage(argv);
                exit(EXIT_SUCCESS);
            case 'd':
                if (strncmp(optarg, "GPU", 3) == 0) device_type = CL_DEVICE_TYPE_GPU;
                else if (strncmp(optarg, "CPU", 3) == 0) device_type = CL_DEVICE_TYPE_CPU;
                else if (strncmp(optarg, "ALL", 3) == 0) device_type = CL_DEVICE_TYPE_ALL;
                else fprintf(stderr, "Unknown device type '%s', using 'ALL' as default ...\n", optarg);
                break;
            case 'p':
                if (strncmp(optarg, "AMD", 3) == 0) platform_name = AMD;
                else if (strncmp(optarg, "APPLE", 3) == 0) platform_name = APPLE;
                else if (strncmp(optarg, "ARM", 3) == 0) platform_name = ARM;
                else if (strncmp(optarg, "INTEL", 3) == 0) platform_name = INTEL;
                else if (strncmp(optarg, "NVIDIA", 3) == 0) platform_name = NVIDIA;
                else fprintf(stderr, "Unknown platform name '%s', using 'ALL' as default ...\n", optarg);
                break;
            case 'k':
                found_kernel_name = true;
                kernel_name = optarg;
                break;
            case 'f':
                found_file_name = true;
                file_name = optarg;
                break;
            case 'i':
                build_includes += "-I";
                build_includes += optarg;
                build_includes += " ";
                break;
            default: /* '?' */
                fprintf(stderr, "Wrong call syntax!\n");
                usage(argv);
                exit(EXIT_FAILURE);
        }
    }

    if (!found_file_name) {
        fprintf(stderr, "No OpenCL input file specified ...\n");
        usage(argv);
        exit(EXIT_FAILURE);
    }
    if (!found_kernel_name) {
        fprintf(stderr, "No OpenCL kernel specified ...\n");
        usage(argv);
        exit(EXIT_FAILURE);
    }

    hipaccInitPlatformsAndDevices(device_type, platform_name);
    hipaccCreateContextsAndCommandQueues();

    hipaccBuildProgramAndKernel(file_name, kernel_name, print_compile_progress, dump_binary, print_log, build_options.c_str(), build_includes.c_str());

    if (platform_name == AMD) {
        // compile OpenCL kernel in order to get resource usage
        // SQ_PGM_RESOURCES:NUM_GPRS = 25
        // SQ_PGM_RESOURCES:STACK_SIZE = 5
        // SQ_LDS_ALLOC:SIZE = 0x00000000
        #ifdef CL_VERSION_1_2
        std::string kernel_glob_command = "ls *_" + kernel_name + ".isa";
        #else
        std::string kernel_glob_command = "ls " + kernel_name + "_*.isa";
        #endif

        char glob_line[FILENAME_MAX];
        std::string kernel_file_name;
        FILE *fpipe;

        if (!(fpipe = (FILE*)popen(kernel_glob_command.c_str(), "r"))) {
            perror("Problems with pipe");
            exit(EXIT_FAILURE);
        }

        while (fgets(glob_line, sizeof(char) * FILENAME_MAX, fpipe)) {
            // trim whitespace
            kernel_file_name = glob_line;
            kernel_file_name.erase(std::remove_if(kernel_file_name.begin(), kernel_file_name.end(), isspace), kernel_file_name.end());
        }
        pclose(fpipe);

        std::ifstream file;
        file.open(kernel_file_name.c_str());
        printf("kernels: '%s'\n", kernel_file_name.c_str());

        std::string line;
        int num_gprs=-1, stack_size=-1, lds_size=-1;
        while (std::getline(file, line)) {
            if (num_gprs < 0) {
                sscanf(line.c_str(), "SQ_PGM_RESOURCES:NUM_GPRS = %d", &num_gprs);
            }
            if (stack_size < 0) {
                sscanf(line.c_str(), "SQ_PGM_RESOURCES:STACK_SIZE = %d", &stack_size);
            }
            if (lds_size < 0) {
                sscanf(line.c_str(), "SQ_LDS_ALLOC:SIZE = %i", &lds_size);
            }
        }
        if (num_gprs < 0 || stack_size < 0 || lds_size < 0) {
            printf("isa error while determining resource usage : Used %d gprs, %d bytes lds, stack size: %d\n", num_gprs, lds_size, stack_size);
        } else {
            printf("isa info : Used %d gprs, %d bytes lds, stack size: %d\n", num_gprs, lds_size, stack_size);
        }
        fflush(stdout);
    }

    return EXIT_SUCCESS;
}

<commit_msg>Add support for Accelerators in OpenCL.<commit_after>//
// Copyright (c) 2012, University of Erlangen-Nuremberg
// Copyright (c) 2012, Siemens AG
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//

/*
 * Tool that compiles a OpenCL kernel in order to determine the resource usage
 */

#include <algorithm>
#include <glob.h>
#include <stdio.h>
#include <unistd.h>

#include "hipacc_ocl.hpp"

void usage(char **argv) {
    fprintf(stderr, "Usage: %s [-h] [-d ACC|CPU|GPU|ALL] [-p AMD|APPLE|ARM|INTEL|NVIDIA|ALL] [-i <include_dir>] -k <kernel_name> -f <opencl_file>\n", argv[0]);
}

int main(int argc, char *argv[]) {
    int option = 0;
    cl_device_type device_type = CL_DEVICE_TYPE_ALL;
    cl_platform_name platform_name = ALL;
    std::string kernel_name;
    std::string file_name;
    std::string build_options = "";
    std::string build_includes = "";
    bool found_kernel_name = false, found_file_name = false, print_compile_progress = false, dump_binary = false, print_log = true;

    // scan command-line options
    while ((option = getopt(argc, (char * const *)argv, "hd:p:k:f:i:")) != -1) {
        switch (option) {
            case 'h':
                fprintf(stderr, "Compile OpenCL kernel for given Platform and/or device type.\n");
                usage(argv);
                exit(EXIT_SUCCESS);
            case 'd':
                if (strncmp(optarg, "ACC", 3) == 0) device_type = CL_DEVICE_TYPE_ACCELERATOR;
                else if (strncmp(optarg, "CPU", 3) == 0) device_type = CL_DEVICE_TYPE_CPU;
                else if (strncmp(optarg, "GPU", 3) == 0) device_type = CL_DEVICE_TYPE_GPU;
                else if (strncmp(optarg, "ALL", 3) == 0) device_type = CL_DEVICE_TYPE_ALL;
                else fprintf(stderr, "Unknown device type '%s', using 'ALL' as default ...\n", optarg);
                break;
            case 'p':
                if (strncmp(optarg, "AMD", 3) == 0) platform_name = AMD;
                else if (strncmp(optarg, "APPLE", 3) == 0) platform_name = APPLE;
                else if (strncmp(optarg, "ARM", 3) == 0) platform_name = ARM;
                else if (strncmp(optarg, "INTEL", 3) == 0) platform_name = INTEL;
                else if (strncmp(optarg, "NVIDIA", 3) == 0) platform_name = NVIDIA;
                else fprintf(stderr, "Unknown platform name '%s', using 'ALL' as default ...\n", optarg);
                break;
            case 'k':
                found_kernel_name = true;
                kernel_name = optarg;
                break;
            case 'f':
                found_file_name = true;
                file_name = optarg;
                break;
            case 'i':
                build_includes += "-I";
                build_includes += optarg;
                build_includes += " ";
                break;
            default: /* '?' */
                fprintf(stderr, "Wrong call syntax!\n");
                usage(argv);
                exit(EXIT_FAILURE);
        }
    }

    if (!found_file_name) {
        fprintf(stderr, "No OpenCL input file specified ...\n");
        usage(argv);
        exit(EXIT_FAILURE);
    }
    if (!found_kernel_name) {
        fprintf(stderr, "No OpenCL kernel specified ...\n");
        usage(argv);
        exit(EXIT_FAILURE);
    }

    hipaccInitPlatformsAndDevices(device_type, platform_name);
    hipaccCreateContextsAndCommandQueues();

    hipaccBuildProgramAndKernel(file_name, kernel_name, print_compile_progress, dump_binary, print_log, build_options.c_str(), build_includes.c_str());

    if (platform_name == AMD) {
        // compile OpenCL kernel in order to get resource usage
        // SQ_PGM_RESOURCES:NUM_GPRS = 25
        // SQ_PGM_RESOURCES:STACK_SIZE = 5
        // SQ_LDS_ALLOC:SIZE = 0x00000000
        #ifdef CL_VERSION_1_2
        std::string kernel_glob_command = "ls *_" + kernel_name + ".isa";
        #else
        std::string kernel_glob_command = "ls " + kernel_name + "_*.isa";
        #endif

        char glob_line[FILENAME_MAX];
        std::string kernel_file_name;
        FILE *fpipe;

        if (!(fpipe = (FILE*)popen(kernel_glob_command.c_str(), "r"))) {
            perror("Problems with pipe");
            exit(EXIT_FAILURE);
        }

        while (fgets(glob_line, sizeof(char) * FILENAME_MAX, fpipe)) {
            // trim whitespace
            kernel_file_name = glob_line;
            kernel_file_name.erase(std::remove_if(kernel_file_name.begin(), kernel_file_name.end(), isspace), kernel_file_name.end());
        }
        pclose(fpipe);

        std::ifstream file;
        file.open(kernel_file_name.c_str());
        printf("kernels: '%s'\n", kernel_file_name.c_str());

        std::string line;
        int num_gprs=-1, stack_size=-1, lds_size=-1;
        while (std::getline(file, line)) {
            if (num_gprs < 0) {
                sscanf(line.c_str(), "SQ_PGM_RESOURCES:NUM_GPRS = %d", &num_gprs);
            }
            if (stack_size < 0) {
                sscanf(line.c_str(), "SQ_PGM_RESOURCES:STACK_SIZE = %d", &stack_size);
            }
            if (lds_size < 0) {
                sscanf(line.c_str(), "SQ_LDS_ALLOC:SIZE = %i", &lds_size);
            }
        }
        if (num_gprs < 0 || stack_size < 0 || lds_size < 0) {
            printf("isa error while determining resource usage : Used %d gprs, %d bytes lds, stack size: %d\n", num_gprs, lds_size, stack_size);
        } else {
            printf("isa info : Used %d gprs, %d bytes lds, stack size: %d\n", num_gprs, lds_size, stack_size);
        }
        fflush(stdout);
    }

    return EXIT_SUCCESS;
}

<|endoftext|>


<commit_before>// Copyright (c) 2009 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/gtk/download_shelf_gtk.h"

#include "app/l10n_util.h"
#include "app/resource_bundle.h"
#include "base/gfx/gtk_util.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/download/download_item_model.h"
#include "chrome/browser/download/download_util.h"
#include "chrome/browser/gtk/browser_window_gtk.h"
#include "chrome/browser/gtk/custom_button.h"
#include "chrome/browser/gtk/download_item_gtk.h"
#include "chrome/browser/gtk/gtk_chrome_link_button.h"
#include "chrome/browser/gtk/slide_animator_gtk.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/gtk_util.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"

namespace {

// The height of the download items.
const int kDownloadItemHeight = download_util::kSmallProgressIconSize;

// Padding between the download widgets.
const int kDownloadItemPadding = 10;

// Padding between the top/bottom of the download widgets and the edge of the
// shelf.
const int kTopBottomPadding = 4;

// Padding between the left side of the shelf and the first download item.
const int kLeftPadding = 2;

// Padding between the right side of the shelf and the close button.
const int kRightPadding = 10;

// The background color of the shelf.
const GdkColor kBackgroundColor = GDK_COLOR_RGB(230, 237, 244);

// Border color (the top pixel of the shelf).
const GdkColor kBorderColor = GDK_COLOR_RGB(214, 214, 214);

// Speed of the shelf show/hide animation.
const int kShelfAnimationDurationMs = 120;

}  // namespace

DownloadShelfGtk::DownloadShelfGtk(Browser* browser, GtkWidget* parent)
    : DownloadShelf(browser),
      is_showing_(false) {
  // Logically, the shelf is a vbox that contains two children: a one pixel
  // tall event box, which serves as the top border, and an hbox, which holds
  // the download items and other shelf widgets (close button, show-all-
  // downloads link).
  // To make things pretty, we have to add a few more widgets. To get padding
  // right, we stick the hbox in an alignment. We put that alignment in an
  // event box so we can color the background.

  // Create the top border.
  GtkWidget* top_border = gtk_event_box_new();
  gtk_widget_set_size_request(GTK_WIDGET(top_border), 0, 1);
  gtk_widget_modify_bg(top_border, GTK_STATE_NORMAL, &kBorderColor);

  // Create |hbox_|.
  hbox_.Own(gtk_hbox_new(FALSE, kDownloadItemPadding));
  gtk_widget_set_size_request(hbox_.get(), -1, kDownloadItemHeight);

  // Get the padding and background color for |hbox_| right.
  GtkWidget* padding = gtk_alignment_new(0, 0, 1, 1);
  // Subtract 1 from top spacing to account for top border.
  gtk_alignment_set_padding(GTK_ALIGNMENT(padding),
      kTopBottomPadding - 1, kTopBottomPadding, kLeftPadding, kRightPadding);
  GtkWidget* padding_bg = gtk_event_box_new();
  gtk_container_add(GTK_CONTAINER(padding_bg), padding);
  gtk_container_add(GTK_CONTAINER(padding), hbox_.get());
  gtk_widget_modify_bg(padding_bg, GTK_STATE_NORMAL, &kBackgroundColor);

  shelf_.Own(gtk_vbox_new(FALSE, 0));
  gtk_box_pack_start(GTK_BOX(shelf_.get()), top_border, FALSE, FALSE, 0);
  gtk_box_pack_start(GTK_BOX(shelf_.get()), padding_bg, FALSE, FALSE, 0);

  // Create and pack the close button.
  close_button_.reset(CustomDrawButton::CloseButton());
  gtk_util::CenterWidgetInHBox(hbox_.get(), close_button_->widget(), true, 0);
  g_signal_connect(close_button_->widget(), "clicked",
                   G_CALLBACK(OnButtonClick), this);

  // Create the "Show all downloads..." link and connect to the click event.
  std::string link_text =
      l10n_util::GetStringUTF8(IDS_SHOW_ALL_DOWNLOADS);
  GtkWidget* link_button = gtk_chrome_link_button_new(link_text.c_str());
  g_signal_connect(link_button, "clicked",
                   G_CALLBACK(OnButtonClick), this);
  // Until we switch to vector graphics, force the font size.
  // 13.4px == 10pt @ 96dpi
  gtk_util::ForceFontSizePixels(GTK_CHROME_LINK_BUTTON(link_button)->label,
                                13.4);

  // Make the download arrow icon.
  ResourceBundle& rb = ResourceBundle::GetSharedInstance();
  GdkPixbuf* download_pixbuf = rb.GetPixbufNamed(IDR_DOWNLOADS_FAVICON);
  GtkWidget* download_image = gtk_image_new_from_pixbuf(download_pixbuf);

  // Pack the link and the icon in an hbox.
  link_hbox_ = gtk_hbox_new(FALSE, 5);
  gtk_util::CenterWidgetInHBox(link_hbox_, download_image, false, 0);
  gtk_util::CenterWidgetInHBox(link_hbox_, link_button, false, 0);
  gtk_box_pack_end(GTK_BOX(hbox_.get()), link_hbox_, FALSE, FALSE, 0);

  slide_widget_.reset(new SlideAnimatorGtk(shelf_.get(),
                                           SlideAnimatorGtk::UP,
                                           kShelfAnimationDurationMs,
                                           false, NULL));
  gtk_widget_show_all(shelf_.get());

  // Stick ourselves at the bottom of the parent browser.
  gtk_box_pack_end(GTK_BOX(parent), slide_widget_->widget(),
                   FALSE, FALSE, 0);
  slide_widget_->Open();
}

DownloadShelfGtk::~DownloadShelfGtk() {
  for (std::vector<DownloadItemGtk*>::iterator iter = download_items_.begin();
       iter != download_items_.end(); ++iter) {
    delete *iter;
  }

  shelf_.Destroy();
  hbox_.Destroy();
}

void DownloadShelfGtk::AddDownload(BaseDownloadItemModel* download_model_) {
  download_items_.push_back(new DownloadItemGtk(this, download_model_));
  Show();
}

bool DownloadShelfGtk::IsShowing() const {
  return slide_widget_->IsShowing();
}

bool DownloadShelfGtk::IsClosing() const {
  return slide_widget_->IsClosing();
}

void DownloadShelfGtk::Show() {
  slide_widget_->Open();
}

void DownloadShelfGtk::Close() {
  slide_widget_->Close();

  // TODO(estade): Remove. The status bubble should query its window instead.
  browser_->UpdateDownloadShelfVisibility(false);
}

int DownloadShelfGtk::GetHeight() const {
  return slide_widget_->widget()->allocation.height;
}

void DownloadShelfGtk::RemoveDownloadItem(DownloadItemGtk* download_item) {
  DCHECK(download_item);
  std::vector<DownloadItemGtk*>::iterator i =
      find(download_items_.begin(), download_items_.end(), download_item);
  DCHECK(i != download_items_.end());
  download_items_.erase(i);
  delete download_item;
  if (download_items_.empty()) {
    slide_widget_->CloseWithoutAnimation();

    // TODO(estade): Remove. The status bubble should query its window instead.
    browser_->UpdateDownloadShelfVisibility(false);
  }
}

GtkWidget* DownloadShelfGtk::GetRightBoundingWidget() const {
  return link_hbox_;
}

GtkWidget* DownloadShelfGtk::GetHBox() const {
  return hbox_.get();
}

// static
void DownloadShelfGtk::OnButtonClick(GtkWidget* button,
                                     DownloadShelfGtk* shelf) {
  if (button == shelf->close_button_->widget()) {
    shelf->Close();
  } else {
    // The link button was clicked.
    shelf->ShowAllDownloads();
  }
}
<commit_msg>Fix download shelf z ordering during close.<commit_after>// Copyright (c) 2009 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/gtk/download_shelf_gtk.h"

#include "app/l10n_util.h"
#include "app/resource_bundle.h"
#include "base/gfx/gtk_util.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/download/download_item_model.h"
#include "chrome/browser/download/download_util.h"
#include "chrome/browser/gtk/browser_window_gtk.h"
#include "chrome/browser/gtk/custom_button.h"
#include "chrome/browser/gtk/download_item_gtk.h"
#include "chrome/browser/gtk/gtk_chrome_link_button.h"
#include "chrome/browser/gtk/slide_animator_gtk.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/gtk_util.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"

namespace {

// The height of the download items.
const int kDownloadItemHeight = download_util::kSmallProgressIconSize;

// Padding between the download widgets.
const int kDownloadItemPadding = 10;

// Padding between the top/bottom of the download widgets and the edge of the
// shelf.
const int kTopBottomPadding = 4;

// Padding between the left side of the shelf and the first download item.
const int kLeftPadding = 2;

// Padding between the right side of the shelf and the close button.
const int kRightPadding = 10;

// The background color of the shelf.
const GdkColor kBackgroundColor = GDK_COLOR_RGB(230, 237, 244);

// Border color (the top pixel of the shelf).
const GdkColor kBorderColor = GDK_COLOR_RGB(214, 214, 214);

// Speed of the shelf show/hide animation.
const int kShelfAnimationDurationMs = 120;

}  // namespace

DownloadShelfGtk::DownloadShelfGtk(Browser* browser, GtkWidget* parent)
    : DownloadShelf(browser),
      is_showing_(false) {
  // Logically, the shelf is a vbox that contains two children: a one pixel
  // tall event box, which serves as the top border, and an hbox, which holds
  // the download items and other shelf widgets (close button, show-all-
  // downloads link).
  // To make things pretty, we have to add a few more widgets. To get padding
  // right, we stick the hbox in an alignment. We put that alignment in an
  // event box so we can color the background.

  // Create the top border.
  GtkWidget* top_border = gtk_event_box_new();
  gtk_widget_set_size_request(GTK_WIDGET(top_border), 0, 1);
  gtk_widget_modify_bg(top_border, GTK_STATE_NORMAL, &kBorderColor);

  // Create |hbox_|.
  hbox_.Own(gtk_hbox_new(FALSE, kDownloadItemPadding));
  gtk_widget_set_size_request(hbox_.get(), -1, kDownloadItemHeight);

  // Get the padding and background color for |hbox_| right.
  GtkWidget* padding = gtk_alignment_new(0, 0, 1, 1);
  // Subtract 1 from top spacing to account for top border.
  gtk_alignment_set_padding(GTK_ALIGNMENT(padding),
      kTopBottomPadding - 1, kTopBottomPadding, kLeftPadding, kRightPadding);
  GtkWidget* padding_bg = gtk_event_box_new();
  gtk_container_add(GTK_CONTAINER(padding_bg), padding);
  gtk_container_add(GTK_CONTAINER(padding), hbox_.get());
  gtk_widget_modify_bg(padding_bg, GTK_STATE_NORMAL, &kBackgroundColor);

  GtkWidget* vbox = gtk_vbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), top_border, FALSE, FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), padding_bg, FALSE, FALSE, 0);

  // Put the shelf in an event box so it gets its own window, which makes it
  // easier to get z-ordering right.
  shelf_.Own(gtk_event_box_new());
  gtk_container_add(GTK_CONTAINER(shelf_.get()), vbox);

  // Create and pack the close button.
  close_button_.reset(CustomDrawButton::CloseButton());
  gtk_util::CenterWidgetInHBox(hbox_.get(), close_button_->widget(), true, 0);
  g_signal_connect(close_button_->widget(), "clicked",
                   G_CALLBACK(OnButtonClick), this);

  // Create the "Show all downloads..." link and connect to the click event.
  std::string link_text =
      l10n_util::GetStringUTF8(IDS_SHOW_ALL_DOWNLOADS);
  GtkWidget* link_button = gtk_chrome_link_button_new(link_text.c_str());
  g_signal_connect(link_button, "clicked",
                   G_CALLBACK(OnButtonClick), this);
  // Until we switch to vector graphics, force the font size.
  // 13.4px == 10pt @ 96dpi
  gtk_util::ForceFontSizePixels(GTK_CHROME_LINK_BUTTON(link_button)->label,
                                13.4);

  // Make the download arrow icon.
  ResourceBundle& rb = ResourceBundle::GetSharedInstance();
  GdkPixbuf* download_pixbuf = rb.GetPixbufNamed(IDR_DOWNLOADS_FAVICON);
  GtkWidget* download_image = gtk_image_new_from_pixbuf(download_pixbuf);

  // Pack the link and the icon in an hbox.
  link_hbox_ = gtk_hbox_new(FALSE, 5);
  gtk_util::CenterWidgetInHBox(link_hbox_, download_image, false, 0);
  gtk_util::CenterWidgetInHBox(link_hbox_, link_button, false, 0);
  gtk_box_pack_end(GTK_BOX(hbox_.get()), link_hbox_, FALSE, FALSE, 0);

  slide_widget_.reset(new SlideAnimatorGtk(shelf_.get(),
                                           SlideAnimatorGtk::UP,
                                           kShelfAnimationDurationMs,
                                           false, NULL));
  gtk_widget_show_all(shelf_.get());

  // Stick ourselves at the bottom of the parent browser.
  gtk_box_pack_end(GTK_BOX(parent), slide_widget_->widget(),
                   FALSE, FALSE, 0);
  slide_widget_->Open();
}

DownloadShelfGtk::~DownloadShelfGtk() {
  for (std::vector<DownloadItemGtk*>::iterator iter = download_items_.begin();
       iter != download_items_.end(); ++iter) {
    delete *iter;
  }

  shelf_.Destroy();
  hbox_.Destroy();
}

void DownloadShelfGtk::AddDownload(BaseDownloadItemModel* download_model_) {
  download_items_.push_back(new DownloadItemGtk(this, download_model_));
  Show();
}

bool DownloadShelfGtk::IsShowing() const {
  return slide_widget_->IsShowing();
}

bool DownloadShelfGtk::IsClosing() const {
  return slide_widget_->IsClosing();
}

void DownloadShelfGtk::Show() {
  slide_widget_->Open();
}

void DownloadShelfGtk::Close() {
  // When we are closing, we can vertically overlap the render view. Make sure
  // we are on top.
  gdk_window_raise(shelf_.get()->window);
  slide_widget_->Close();

  // TODO(estade): Remove. The status bubble should query its window instead.
  browser_->UpdateDownloadShelfVisibility(false);
}

int DownloadShelfGtk::GetHeight() const {
  return slide_widget_->widget()->allocation.height;
}

void DownloadShelfGtk::RemoveDownloadItem(DownloadItemGtk* download_item) {
  DCHECK(download_item);
  std::vector<DownloadItemGtk*>::iterator i =
      find(download_items_.begin(), download_items_.end(), download_item);
  DCHECK(i != download_items_.end());
  download_items_.erase(i);
  delete download_item;
  if (download_items_.empty()) {
    slide_widget_->CloseWithoutAnimation();

    // TODO(estade): Remove. The status bubble should query its window instead.
    browser_->UpdateDownloadShelfVisibility(false);
  }
}

GtkWidget* DownloadShelfGtk::GetRightBoundingWidget() const {
  return link_hbox_;
}

GtkWidget* DownloadShelfGtk::GetHBox() const {
  return hbox_.get();
}

// static
void DownloadShelfGtk::OnButtonClick(GtkWidget* button,
                                     DownloadShelfGtk* shelf) {
  if (button == shelf->close_button_->widget()) {
    shelf->Close();
  } else {
    // The link button was clicked.
    shelf->ShowAllDownloads();
  }
}
<|endoftext|>