// Copyright (c) 2012 Intel Corp
// Copyright (c) 2012 The Chromium Authors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell co
// pies of the Software, and to permit persons to whom the Software is furnished
//  to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in al
// l copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM
// PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNES
// S FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
//  OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WH
// ETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
//  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#include "content/nw/src/api/menu/menu.h"

#include "base/values.h"
#include "base/strings/utf_string_conversions.h"
#include "content/nw/src/api/object_manager.h"
#include "content/nw/src/api/menuitem/menuitem.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "extensions/browser/app_window/app_window.h"
#include "skia/ext/image_operations.h"
#include "ui/aura/client/screen_position_client.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/views/controls/menu/menu_runner.h"
#include "ui/views/widget/widget.h"
#include "ui/views/focus/focus_manager.h"
#include "vector"

namespace ui {

NwMenuModel::NwMenuModel(Delegate* delegate) : SimpleMenuModel(delegate) {
}

bool NwMenuModel::HasIcons() const {
  // Always return false, see the comment about |NwMenuModel|.
  return false;
}

} // namespace ui

namespace nw {

void Menu::Create(const base::DictionaryValue& option) {
  is_menu_modified_ = true;
  menu_delegate_.reset(new MenuDelegate(object_manager()));
  menu_model_.reset(new ui::NwMenuModel(menu_delegate_.get()));

  focus_manager_ = NULL;
  window_ = NULL;

  std::string type;

  menu_items_.empty();
}

void Menu::Destroy() {
}

void Menu::Append(MenuItem* menu_item) {
  if (menu_item->submenu_)
    menu_model_->AddSubMenu(menu_item->id(), menu_item->label_,
                            menu_item->submenu_->menu_model_.get());
  else if (menu_item->type_ == "normal")
    menu_model_->AddItem(menu_item->id(), menu_item->label_);
  else if (menu_item->type_ == "checkbox")
    menu_model_->AddCheckItem(menu_item->id(), menu_item->label_);
  else if (menu_item->type_ == "separator")
    menu_model_->AddSeparator(ui::NORMAL_SEPARATOR);

  is_menu_modified_ = true;
  menu_items_.push_back(menu_item);
  menu_item->menu_ = this;
}

void Menu::Insert(MenuItem* menu_item, int pos) {
  if (pos < 0 || pos > menu_items_.size()) return;

  if (menu_item->submenu_)
    menu_model_->InsertSubMenuAt(pos, menu_item->id(), menu_item->label_,
                                 menu_item->submenu_->menu_model_.get());
  else if (menu_item->type_ == "normal")
    menu_model_->InsertItemAt(pos, menu_item->id(), menu_item->label_);
  else if (menu_item->type_ == "checkbox")
    menu_model_->InsertCheckItemAt(pos, menu_item->id(), menu_item->label_);
  else if (menu_item->type_ == "separator")
    menu_model_->InsertSeparatorAt(pos, ui::NORMAL_SEPARATOR);

  is_menu_modified_ = true;
  menu_item->menu_ = this;
  menu_items_.insert(menu_items_.begin() + pos, menu_item);
}

void Menu::Remove(MenuItem* menu_item, int pos) {
  if (pos < 0 || pos >= menu_items_.size()) return;

  menu_model_->RemoveItemAt(pos);
  menu_items_.erase(menu_items_.begin() + pos);
  is_menu_modified_ = true;
  menu_item->menu_ = NULL;
  menu_item->RemoveKeys();
}

void Menu::Popup(int x, int y, content::RenderFrameHost* rfh) {
  // Rebuild();

  // Map point from document to screen.
  gfx::Point screen_point(x, y);

  // Convert from content coordinates to window coordinates.
  // This code copied from chrome_web_contents_view_delegate_views.cc
  aura::Window* target_window = GetActiveNativeView(rfh);
  aura::Window* root_window = target_window->GetRootWindow();
  views::Widget* top_level_widget =
    views::Widget::GetTopLevelWidgetForNativeView(target_window);
  aura::client::ScreenPositionClient* screen_position_client =
        aura::client::GetScreenPositionClient(root_window);
  if (screen_position_client) {
    screen_position_client->ConvertPointToScreen(target_window,
             &screen_point);
  }
  set_delay_destruction(true);
  views::MenuRunner runner(menu_model_.get(), views::MenuRunner::CONTEXT_MENU);
  ignore_result(
      runner.RunMenuAt(top_level_widget,
                       NULL,
                       gfx::Rect(screen_point, gfx::Size()),
                       views::MENU_ANCHOR_TOPRIGHT,
                       ui::MENU_SOURCE_NONE));
  set_delay_destruction(false);
  if (pending_destruction())
    object_manager_->OnDeallocateObject(id_);
}

void Menu::UpdateKeys(views::FocusManager *focus_manager){
  if (focus_manager == NULL){
    return ;
  } else {
    focus_manager_ = focus_manager;
    for(auto item : menu_items_) {
      item->UpdateKeys(focus_manager);
    }
  }
}

void Menu::RemoveKeys() {
  if (!focus_manager_) return;

  for(auto item: menu_items_) {
    item->RemoveKeys();
  }

  focus_manager_ = NULL;
}

void Menu::UpdateStates() {
}

aura::Window* Menu::GetActiveNativeView(content::RenderFrameHost* rfh) {
  content::WebContents* web_contents =
    content::WebContents::FromRenderFrameHost(rfh);
  if (!web_contents) {
    LOG(ERROR) << "Menu: couldn't find WebContents";
    return NULL;
  }
  return web_contents->GetFullscreenRenderWidgetHostView()
             ? web_contents->GetFullscreenRenderWidgetHostView()
                   ->GetNativeView()
             : web_contents->GetNativeView();
}

}  // namespace nw


wrappy

Callback wrapping utility


// Copyright (c) 2011 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.

#ifndef CHROME_BROWSER_UI_VIEWS_LOGIN_VIEW_H_
#define CHROME_BROWSER_UI_VIEWS_LOGIN_VIEW_H_

#include "base/compiler_specific.h"
#include "ui/views/view.h"

namespace views {
class Label;
class Textfield;
}

// This class is responsible for displaying the contents of a login window
// for HTTP/FTP authentication.
class LoginView : public views::View {
 public:
  // |model| is observed for the entire lifetime of the LoginView.
  // Therefore |model| should not be destroyed before the LoginView object.
  LoginView(const base::string16& explanation);
  ~LoginView() final;

  // Access the data in the username/password text fields.
  const base::string16& GetUsername() const;
  const base::string16& GetPassword() const;

  // Used by LoginHandlerWin to set the initial focus.
  views::View* GetInitiallyFocusedView();

 private:
  // Non-owning refs to the input text fields.
  views::Textfield* username_field_;
  views::Textfield* password_field_;

  // Button labels
  views::Label* username_label_;
  views::Label* password_label_;

  // Authentication message.
  views::Label* message_label_;

  DISALLOW_COPY_AND_ASSIGN(LoginView);
};

#endif  // CHROME_BROWSER_UI_VIEWS_LOGIN_VIEW_H_


// Copyright (c) 2012 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/printing/print_job_worker.h"

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/message_loop/message_loop.h"
#include "base/values.h"
#include "content/nw/src/browser/printing/print_job.h"
#include "chrome/browser/printing/printing_ui_web_contents_observer.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/common/content_client.h"
#include "grit/nw_resources.h"
#include "printing/backend/print_backend.h"
#include "printing/print_job_constants.h"
#include "printing/printed_document.h"
#include "printing/printed_page.h"
#include "printing/printing_utils.h"
#include "ui/base/l10n/l10n_util.h"

using content::BrowserThread;
using base::MessageLoop;

namespace {

// Helper function to ensure |owner| is valid until at least |callback| returns.
void HoldRefCallback(const scoped_refptr<printing::PrintJobWorkerOwner>& owner,
                     const base::Closure& callback) {
  callback.Run();
}

}  // namespace

namespace printing {

void NotificationCallback(PrintJobWorkerOwner* print_job,
                          JobEventDetails::Type detail_type,
                          PrintedDocument* document,
                          PrintedPage* page) {
  JobEventDetails* details = new JobEventDetails(detail_type, document, page);
  content::NotificationService::current()->Notify(
      content::NOTIFICATION_PRINT_JOB_EVENT,
      // We know that is is a PrintJob object in this circumstance.
      content::Source<PrintJob>(static_cast<PrintJob*>(print_job)),
      content::Details<JobEventDetails>(details));
}

PrintJobWorker::PrintJobWorker(PrintJobWorkerOwner* owner)
    : Thread("Printing_Worker"),
      owner_(owner),
      weak_factory_(this) {
  // The object is created in the IO thread.
  DCHECK_EQ(owner_->message_loop(), MessageLoop::current());

  printing_context_.reset(PrintingContext::Create(
                                                  content::GetContentClient()->browser()->GetApplicationLocale()));
}

PrintJobWorker::~PrintJobWorker() {
  // The object is normally deleted in the UI thread, but when the user
  // cancels printing or in the case of print preview, the worker is destroyed
  // on the I/O thread.
  DCHECK_EQ(owner_->message_loop(), MessageLoop::current());
  Stop();
}

void PrintJobWorker::SetNewOwner(PrintJobWorkerOwner* new_owner) {
  DCHECK(page_number_ == PageNumber::npos());
  owner_ = new_owner;
}

void PrintJobWorker::SetPrintDestination(
    PrintDestinationInterface* destination) {
  destination_ = destination;
}

void PrintJobWorker::GetSettings(
    bool ask_user_for_settings,
    scoped_ptr<PrintingUIWebContentsObserver> web_contents_observer,
    int document_page_count,
    bool has_selection,
    MarginType margin_type) {
  DCHECK_EQ(message_loop(), MessageLoop::current());
  DCHECK_EQ(page_number_, PageNumber::npos());

  // Recursive task processing is needed for the dialog in case it needs to be
  // destroyed by a task.
  // TODO(thestig): This code is wrong. SetNestableTasksAllowed(true) is needed
  // on the thread where the PrintDlgEx is called, and definitely both calls
  // should happen on the same thread. See http://crbug.com/73466
  // MessageLoop::current()->SetNestableTasksAllowed(true);
  printing_context_->set_margin_type(margin_type);

  // When we delegate to a destination, we don't ask the user for settings.
  // TODO(mad): Ask the destination for settings.
  if (ask_user_for_settings && destination_.get() == NULL) {
    BrowserThread::PostTask(
        BrowserThread::UI, FROM_HERE,
        base::Bind(&HoldRefCallback, make_scoped_refptr(owner_),
                   base::Bind(&PrintJobWorker::GetSettingsWithUI,
                              base::Unretained(this),
                              base::Passed(&web_contents_observer),
                              document_page_count,
                              has_selection)));
  } else {
    BrowserThread::PostTask(
        BrowserThread::UI, FROM_HERE,
        base::Bind(&HoldRefCallback, make_scoped_refptr(owner_),
                   base::Bind(&PrintJobWorker::UseDefaultSettings,
                              base::Unretained(this))));
  }
}

void PrintJobWorker::SetSettings(const base::DictionaryValue* const new_settings) {
  DCHECK_EQ(message_loop(), MessageLoop::current());

  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      base::Bind(&HoldRefCallback, make_scoped_refptr(owner_),
                 base::Bind(&PrintJobWorker::UpdatePrintSettings,
                            base::Unretained(this), new_settings)));
}

void PrintJobWorker::UpdatePrintSettings(
    const base::DictionaryValue* const new_settings) {
  PrintingContext::Result result =
    printing_context_->UpdatePrintSettings(*new_settings);
  GetSettingsDone(result);
}

void PrintJobWorker::GetSettingsDone(PrintingContext::Result result) {
  // Most PrintingContext functions may start a message loop and process
  // message recursively, so disable recursive task processing.
  // TODO(thestig): See above comment. SetNestableTasksAllowed(false) needs to
  // be called on the same thread as the previous call.  See
  // http://crbug.com/73466
  // MessageLoop::current()->SetNestableTasksAllowed(false);

  // We can't use OnFailure() here since owner_ may not support notifications.

  // PrintJob will create the new PrintedDocument.
  owner_->message_loop()->PostTask(
      FROM_HERE,
      base::Bind(&PrintJobWorkerOwner::GetSettingsDone,
                 make_scoped_refptr(owner_), printing_context_->settings(),
                 result));
}

void PrintJobWorker::GetSettingsWithUI(
    scoped_ptr<PrintingUIWebContentsObserver> web_contents_observer,
    int document_page_count,
    bool has_selection) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  gfx::NativeView parent_view = web_contents_observer->GetParentView();
  if (!parent_view) {
    GetSettingsWithUIDone(printing::PrintingContext::FAILED);
    return;
  }
  printing_context_->AskUserForSettings(
      parent_view, document_page_count, has_selection,
      base::Bind(&PrintJobWorker::GetSettingsWithUIDone,
                 base::Unretained(this)));
}

void PrintJobWorker::GetSettingsWithUIDone(PrintingContext::Result result) {
  message_loop()->PostTask(
      FROM_HERE,
      base::Bind(&HoldRefCallback, make_scoped_refptr(owner_),
                 base::Bind(&PrintJobWorker::GetSettingsDone,
                            base::Unretained(this), result)));
}

void PrintJobWorker::UseDefaultSettings() {
  PrintingContext::Result result = printing_context_->UseDefaultSettings();
  GetSettingsDone(result);
}

void PrintJobWorker::StartPrinting(PrintedDocument* new_document) {
  DCHECK_EQ(message_loop(), MessageLoop::current());
  DCHECK_EQ(page_number_, PageNumber::npos());
  DCHECK_EQ(document_, new_document);
  DCHECK(document_.get());

  if (!document_.get() || page_number_ != PageNumber::npos() ||
      document_ != new_document) {
    return;
  }

  base::string16 document_name =
      printing::SimplifyDocumentTitle(document_->name());
  if (document_name.empty()) {
    document_name = printing::SimplifyDocumentTitle(
        l10n_util::GetStringUTF16(IDS_DEFAULT_PRINT_DOCUMENT_TITLE));
  }
  PrintingContext::Result result =
      printing_context_->NewDocument(document_name);
  if (result != PrintingContext::OK) {
    OnFailure();
    return;
  }

  // Try to print already cached data. It may already have been generated for
  // the print preview.
  OnNewPage();
  // Don't touch this anymore since the instance could be destroyed. It happens
  // if all the pages are printed a one sweep and the client doesn't have a
  // handle to us anymore. There's a timing issue involved between the worker
  // thread and the UI thread. Take no chance.
}

void PrintJobWorker::OnDocumentChanged(PrintedDocument* new_document) {
  DCHECK_EQ(message_loop(), MessageLoop::current());
  DCHECK_EQ(page_number_, PageNumber::npos());

  if (page_number_ != PageNumber::npos())
    return;

  document_ = new_document;
}

void PrintJobWorker::OnNewPage() {
  if (!document_.get())  // Spurious message.
    return;

  // message_loop() could return NULL when the print job is cancelled.
  DCHECK_EQ(message_loop(), MessageLoop::current());

  if (page_number_ == PageNumber::npos()) {
    // Find first page to print.
    int page_count = document_->page_count();
    if (!page_count) {
      // We still don't know how many pages the document contains. We can't
      // start to print the document yet since the header/footer may refer to
      // the document's page count.
      return;
    }
    // We have enough information to initialize page_number_.
    page_number_.Init(document_->settings(), page_count);
    if (destination_.get() != NULL)
      destination_->SetPageCount(page_count);
  }
  DCHECK_NE(page_number_, PageNumber::npos());

  while (true) {
    // Is the page available?
    scoped_refptr<PrintedPage> page = document_->GetPage(page_number_.ToInt());
    if (!page) {
      // We need to wait for the page to be available.
      MessageLoop::current()->PostDelayedTask(
          FROM_HERE,
          base::Bind(&PrintJobWorker::OnNewPage, weak_factory_.GetWeakPtr()),
          base::TimeDelta::FromMilliseconds(500));
      break;
    }
    // The page is there, print it.
    SpoolPage(page);
    ++page_number_;
    if (page_number_ == PageNumber::npos()) {
      OnDocumentDone();
      // Don't touch this anymore since the instance could be destroyed.
      break;
    }
  }
}

void PrintJobWorker::Cancel() {
  // This is the only function that can be called from any thread.
  printing_context_->Cancel();
  // Cannot touch any member variable since we don't know in which thread
  // context we run.
}

void PrintJobWorker::OnDocumentDone() {
  DCHECK_EQ(message_loop(), MessageLoop::current());
  DCHECK_EQ(page_number_, PageNumber::npos());
  DCHECK(document_.get());

  if (printing_context_->DocumentDone() != PrintingContext::OK) {
    OnFailure();
    return;
  }

  owner_->message_loop()->PostTask(
      FROM_HERE, base::Bind(NotificationCallback, make_scoped_refptr(owner_),
                            JobEventDetails::DOC_DONE, document_,
                            scoped_refptr<PrintedPage>()));

  // Makes sure the variables are reinitialized.
  document_ = NULL;
}

void PrintJobWorker::SpoolPage(PrintedPage* page) {
  DCHECK_EQ(message_loop(), MessageLoop::current());
  DCHECK_NE(page_number_, PageNumber::npos());

  // Signal everyone that the page is about to be printed.
  owner_->message_loop()->PostTask(
      FROM_HERE, base::Bind(NotificationCallback, make_scoped_refptr(owner_),
                            JobEventDetails::NEW_PAGE, document_,
                            make_scoped_refptr(page)));

  // Preprocess.
  if (printing_context_->NewPage() != PrintingContext::OK) {
    OnFailure();
    return;
  }

  if (destination_.get() != NULL) {
    std::vector<uint8> metabytes(page->metafile()->GetDataSize());
    bool success = page->metafile()->GetData(
        reinterpret_cast<void*>(&metabytes[0]), metabytes.size());
    DCHECK(success) << "Failed to get metafile data.";
    destination_->SetPageContent(
        page->page_number(),
        reinterpret_cast<void*>(&metabytes[0]),
        metabytes.size());
    return;
  }

  // Actual printing.
#if defined(OS_WIN) || defined(OS_MACOSX)
  document_->RenderPrintedPage(*page, printing_context_->context());
#elif defined(OS_POSIX)
  document_->RenderPrintedPage(*page, printing_context_.get());
#endif

  // Postprocess.
  if (printing_context_->PageDone() != PrintingContext::OK) {
    OnFailure();
    return;
  }

  // Signal everyone that the page is printed.
  owner_->message_loop()->PostTask(
      FROM_HERE,
      base::Bind(NotificationCallback, make_scoped_refptr(owner_),
                 JobEventDetails::PAGE_DONE, document_,
                 make_scoped_refptr(page)));
}

void PrintJobWorker::OnFailure() {
  DCHECK_EQ(message_loop(), MessageLoop::current());

  // We may loose our last reference by broadcasting the FAILED event.
  scoped_refptr<PrintJobWorkerOwner> handle(owner_);

  owner_->message_loop()->PostTask(
      FROM_HERE, base::Bind(NotificationCallback, make_scoped_refptr(owner_),
                            JobEventDetails::FAILED, document_,
                            scoped_refptr<PrintedPage>()));
  Cancel();

  // Makes sure the variables are reinitialized.
  document_ = NULL;
  page_number_ = PageNumber::npos();
}

}  // namespace printing


keep keep keep keep replace replace keep keep keep keep keep

#include "content/nw/src/api/nw_window_api.h"

#include "base/base64.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "content/public/browser/render_widget_host.h"
#include "chrome/browser/devtools/devtools_window.h"
#include "chrome/browser/extensions/devtools_util.h"
#include "chrome/browser/ui/webui/print_preview/printer_backend_proxy.h"
#include "components/zoom/zoom_controller.h"
#include "content/nw/src/api/menu/menu.h"
#include "content/nw/src/api/object_manager.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "extensions/browser/app_window/app_window_registry.h"
#include "extensions/browser/extension_zoom_request_client.h"
#include "extensions/components/native_app_window/native_app_window_views.h"
#include "extensions/common/error_utils.h"
#include "extensions/common/constants.h"
#include "ui/gfx/codec/jpeg_codec.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/display/display.h"
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/display/screen.h"
#include "content/nw/src/api/nw_current_window_internal.h"

#if defined(OS_WIN)
#include <shobjidl.h>
#include <dwmapi.h>

#include "base/win/windows_version.h"
#include "ui/base/win/hidden_window.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/icon_util.h"
#include "ui/gfx/font_list.h"
#include "ui/gfx/platform_font.h"
#include "ui/display/win/dpi.h"
#include "ui/views/win/hwnd_util.h"
#endif

#if defined(OS_LINUX)
#include "chrome/browser/ui/libgtkui/gtk_ui.h"
#endif

#if defined(OS_LINUX) || defined(OS_WIN)
#include "content/nw/src/browser/menubar_view.h"
#include "content/nw/src/browser/browser_view_layout.h"
using nw::BrowserViewLayout;
#endif

#if defined(OS_MACOSX)
#include "content/nw/src/nw_content_mac.h"
#endif

#include "chrome/browser/ui/webui/print_preview/print_preview_handler.h"

using content::RenderWidgetHost;
using content::RenderWidgetHostView;
using content::WebContents;
using zoom::ZoomController;

using nw::Menu;

#if defined(OS_LINUX) || defined(OS_WIN)
using nw::MenuBarView;
#endif

#if defined(OS_LINUX)
static void SetDeskopEnvironment() {
  static bool runOnce = false;
  if (runOnce) return;
  runOnce = true;

  std::unique_ptr<base::Environment> env(base::Environment::Create());
  std::string name;
  //if (env->GetVar("CHROME_DESKTOP", &name) && !name.empty())
  //  return;

  if (!env->GetVar("NW_DESKTOP", &name) || name.empty())
    name = "nw.desktop";

  env->SetVar("CHROME_DESKTOP", name);
}

#endif

namespace extensions {
namespace {

const char kNoAssociatedAppWindow[] =
    "The context from which the function was called did not have an "
    "associated app window.";
}

static AppWindow* getAppWindow(UIThreadExtensionFunction* func) {
  AppWindowRegistry* registry = AppWindowRegistry::Get(func->browser_context());
  DCHECK(registry);
  content::WebContents* web_contents = func->GetSenderWebContents();
  if (!web_contents) {
    // No need to set an error, since we won't return to the caller anyway if
    // there's no RVH.
    return NULL;
  }
  return registry->GetAppWindowForWebContents(web_contents);
}

#ifdef OS_WIN
static HWND getHWND(AppWindow* window) {
  if (window == NULL) return NULL;
  native_app_window::NativeAppWindowViews* native_app_window_views =
    static_cast<native_app_window::NativeAppWindowViews*>(
    window->GetBaseWindow());
  return views::HWNDForWidget(native_app_window_views->widget()->GetTopLevelWidget());
}
#endif

void NwCurrentWindowInternalCloseFunction::DoClose(AppWindow* window) {
  window->GetBaseWindow()->ForceClose();
}

bool NwCurrentWindowInternalCloseFunction::RunAsync() {
  std::unique_ptr<nwapi::nw_current_window_internal::Close::Params> params(
      nwapi::nw_current_window_internal::Close::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());

  bool force = params->force.get() ? *params->force : false;
  AppWindow* window = getAppWindow(this);
  if (force)
    base::MessageLoop::current()->task_runner()->PostTask(FROM_HERE,
         base::Bind(&NwCurrentWindowInternalCloseFunction::DoClose, window));
  else if (window->NWCanClose())
    base::MessageLoop::current()->task_runner()->PostTask(FROM_HERE,
         base::Bind(&NwCurrentWindowInternalCloseFunction::DoClose, window));

  SendResponse(true);
  return true;
}

void NwCurrentWindowInternalShowDevToolsInternalFunction::OnOpened() {
  SendResponse(true);
}

bool NwCurrentWindowInternalShowDevToolsInternalFunction::RunAsync() {
  content::RenderFrameHost* rfh = render_frame_host();
  content::WebContents* web_contents = content::WebContents::FromRenderFrameHost(rfh);
  scoped_refptr<content::DevToolsAgentHost> agent(
      content::DevToolsAgentHost::GetOrCreateFor(web_contents));
  DevToolsWindow::OpenDevToolsWindow(web_contents);
  DevToolsWindow* devtools_window =
      DevToolsWindow::FindDevToolsWindow(agent.get());
  if (devtools_window)
    devtools_window->SetLoadCompletedCallback(base::Bind(&NwCurrentWindowInternalShowDevToolsInternalFunction::OnOpened, this));
  else
    OnOpened();

  return true;
}

bool NwCurrentWindowInternalCloseDevToolsFunction::RunAsync() {
  content::RenderFrameHost* rfh = render_frame_host();
  content::WebContents* web_contents = content::WebContents::FromRenderFrameHost(rfh);
  scoped_refptr<content::DevToolsAgentHost> agent(
      content::DevToolsAgentHost::GetOrCreateFor(web_contents));
  DevToolsWindow* devtools_window =
      DevToolsWindow::FindDevToolsWindow(agent.get());
  if (devtools_window) {
    devtools_window->Close();
  }
  return true;
}

NwCurrentWindowInternalCapturePageInternalFunction::NwCurrentWindowInternalCapturePageInternalFunction() {
}

NwCurrentWindowInternalCapturePageInternalFunction::~NwCurrentWindowInternalCapturePageInternalFunction() {
}

bool NwCurrentWindowInternalCapturePageInternalFunction::RunAsync() {
  EXTENSION_FUNCTION_VALIDATE(args_);

  std::unique_ptr<ImageDetails> image_details;
  if (args_->GetSize() > 0) {
    base::Value* spec = NULL;
    EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &spec) && spec);
    image_details = ImageDetails::FromValue(*spec);
  }

  content::RenderFrameHost* rfh = render_frame_host();
  WebContents* contents = content::WebContents::FromRenderFrameHost(rfh);
  if (!contents)
    return false;

  // The default format and quality setting used when encoding jpegs.
  const api::extension_types::ImageFormat kDefaultFormat =
      api::extension_types::IMAGE_FORMAT_JPEG;
  const int kDefaultQuality = 90;

  image_format_ = kDefaultFormat;
  image_quality_ = kDefaultQuality;

  if (image_details) {
    if (image_details->format !=
        api::extension_types::IMAGE_FORMAT_NONE)
      image_format_ = image_details->format;
    if (image_details->quality.get())
      image_quality_ = *image_details->quality;
  }

  // TODO(miu): Account for fullscreen render widget?  http://crbug.com/419878
  RenderWidgetHostView* const view = contents->GetRenderWidgetHostView();
  RenderWidgetHost* const host = view ? view->GetRenderWidgetHost() : nullptr;
  if (!view || !host) {
    OnCaptureFailure(FAILURE_REASON_VIEW_INVISIBLE);
    return false;
  }

  // By default, the requested bitmap size is the view size in screen
  // coordinates.  However, if there's more pixel detail available on the
  // current system, increase the requested bitmap size to capture it all.
  const gfx::Size view_size = view->GetViewBounds().size();
  gfx::Size bitmap_size = view_size;
  const gfx::NativeView native_view = view->GetNativeView();
  display::Screen* const screen = display::Screen::GetScreen();
  const float scale =
      screen->GetDisplayNearestWindow(native_view).device_scale_factor();
  if (scale > 1.0f)
    bitmap_size = gfx::ScaleToCeiledSize(view_size, scale);

  host->CopyFromBackingStore(
      gfx::Rect(view_size),
      bitmap_size,
      base::Bind(&NwCurrentWindowInternalCapturePageInternalFunction::CopyFromBackingStoreComplete,
                 this),
      kN32_SkColorType);
  return true;
}

void NwCurrentWindowInternalCapturePageInternalFunction::CopyFromBackingStoreComplete(
    const SkBitmap& bitmap,
    content::ReadbackResponse response) {
  if (response == content::READBACK_SUCCESS) {
    OnCaptureSuccess(bitmap);
    return;
  }
  OnCaptureFailure(FAILURE_REASON_UNKNOWN);
}

void NwCurrentWindowInternalCapturePageInternalFunction::OnCaptureSuccess(const SkBitmap& bitmap) {
  std::vector<unsigned char> data;
  SkAutoLockPixels screen_capture_lock(bitmap);
  bool encoded = false;
  std::string mime_type;
  switch (image_format_) {
    case api::extension_types::IMAGE_FORMAT_JPEG:
      encoded = gfx::JPEGCodec::Encode(
          reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)),
          gfx::JPEGCodec::FORMAT_SkBitmap,
          bitmap.width(),
          bitmap.height(),
          static_cast<int>(bitmap.rowBytes()),
          image_quality_,
          &data);
      mime_type = kMimeTypeJpeg;
      break;
    case api::extension_types::IMAGE_FORMAT_PNG:
      encoded =
          gfx::PNGCodec::EncodeBGRASkBitmap(bitmap,
                                            true,  // Discard transparency.
                                            &data);
      mime_type = kMimeTypePng;
      break;
    default:
      NOTREACHED() << "Invalid image format.";
  }

  if (!encoded) {
    OnCaptureFailure(FAILURE_REASON_ENCODING_FAILED);
    return;
  }

  std::string base64_result;
  base::StringPiece stream_as_string(
                                     reinterpret_cast<const char*>(data.data()), data.size());

  base::Base64Encode(stream_as_string, &base64_result);
  base64_result.insert(
      0, base::StringPrintf("data:%s;base64,", mime_type.c_str()));
  SetResult(base::MakeUnique<base::StringValue>(base64_result.c_str()));
  SendResponse(true);
}

void NwCurrentWindowInternalCapturePageInternalFunction::OnCaptureFailure(FailureReason reason) {
  const char* reason_description = "internal error";
  switch (reason) {
    case FAILURE_REASON_UNKNOWN:
      reason_description = "unknown error";
      break;
    case FAILURE_REASON_ENCODING_FAILED:
      reason_description = "encoding failed";
      break;
    case FAILURE_REASON_VIEW_INVISIBLE:
      reason_description = "view is invisible";
      break;
  }
  error_ = ErrorUtils::FormatErrorMessage("Failed to capture tab: *",
                                          reason_description);
  SendResponse(false);
}

NwCurrentWindowInternalClearMenuFunction::NwCurrentWindowInternalClearMenuFunction() {
}

NwCurrentWindowInternalClearMenuFunction::~NwCurrentWindowInternalClearMenuFunction() {
}

bool NwCurrentWindowInternalClearMenuFunction::RunAsync() {
  AppWindow* window = getAppWindow(this);
  if (!window) {
    error_ = kNoAssociatedAppWindow;
    return false;
  }

#if defined(OS_MACOSX)
  NWChangeAppMenu(NULL);
#endif

#if defined(OS_LINUX) || defined(OS_WIN)
  native_app_window::NativeAppWindowViews* native_app_window_views =
      static_cast<native_app_window::NativeAppWindowViews*>(
          window->GetBaseWindow());

  BrowserViewLayout *browser_view_layout = static_cast<BrowserViewLayout*>(native_app_window_views->GetLayoutManager());
  views::View* menubar = browser_view_layout->menu_bar();
  if (menubar) {
    native_app_window_views->RemoveChildView(menubar);
  }
  browser_view_layout->set_menu_bar(NULL);
  native_app_window_views->layout_();
  native_app_window_views->SchedulePaint();
  window->menu_->RemoveKeys();
  window->menu_ = NULL;
#endif
  return true;
}

NwCurrentWindowInternalSetMenuFunction::NwCurrentWindowInternalSetMenuFunction() {
}

NwCurrentWindowInternalSetMenuFunction::~NwCurrentWindowInternalSetMenuFunction() {
}

bool NwCurrentWindowInternalSetMenuFunction::RunNWSync(base::ListValue* response, std::string* error) {
  int id = 0;
  EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &id));
  AppWindow* window = getAppWindow(this);
  if (!window) {
    error_ = kNoAssociatedAppWindow;
    return false;
  }
  nw::ObjectManager* obj_manager = nw::ObjectManager::Get(browser_context());
  Menu* menu = (Menu*)obj_manager->GetApiObject(id);

  Menu* old_menu = window->menu_;
  window->menu_ = menu;
#if defined(OS_MACOSX)
  response->Append(NWChangeAppMenu(menu));
#endif

#if defined(OS_LINUX) || defined(OS_WIN)
  native_app_window::NativeAppWindowViews* native_app_window_views =
      static_cast<native_app_window::NativeAppWindowViews*>(
          window->GetBaseWindow());

  MenuBarView* menubar = new MenuBarView();
  static_cast<BrowserViewLayout*>(native_app_window_views->GetLayoutManager())->set_menu_bar(menubar);
  native_app_window_views->AddChildView(menubar);
  menubar->UpdateMenu(menu->model());
  native_app_window_views->layout_();
  native_app_window_views->SchedulePaint();
  if (old_menu) old_menu->RemoveKeys();
  menu->UpdateKeys( native_app_window_views->widget()->GetFocusManager() );
  response->Append(std::unique_ptr<base::ListValue>(new base::ListValue()));
#endif
  return true;
}

#if defined(OS_WIN)
static base::win::ScopedHICON createBadgeIcon(const HWND hWnd, const TCHAR *value, const int sizeX, const int sizeY) {
  // canvas for the overlay icon
  gfx::Canvas canvas(gfx::Size(sizeX, sizeY), 1, false);

  // drawing red circle
  SkPaint paint;
  paint.setColor(SK_ColorRED);
  canvas.DrawCircle(gfx::Point(sizeX / 2, sizeY / 2), sizeX / 2, paint);

  // drawing the text
  gfx::PlatformFont *platform_font = gfx::PlatformFont::CreateDefault();
  const int fontSize = sizeY * 0.65f;
  gfx::Font font(platform_font->GetFontName(), fontSize);
  platform_font->Release();
  platform_font = NULL;
  const int yMargin = (sizeY - fontSize) / 2;
  canvas.DrawStringRectWithFlags(value, gfx::FontList(font), SK_ColorWHITE, gfx::Rect(sizeX, fontSize + yMargin + 1), gfx::Canvas::TEXT_ALIGN_CENTER);

  // return the canvas as windows native icon handle
  return std::move(IconUtil::CreateHICONFromSkBitmap(canvas.ExtractImageRep().sk_bitmap()));
}
#endif

#ifndef OS_MACOSX
bool NwCurrentWindowInternalSetBadgeLabelFunction::RunAsync() {
  EXTENSION_FUNCTION_VALIDATE(args_);
  std::string badge;
  EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &badge));
#if defined(OS_WIN)
  base::win::ScopedComPtr<ITaskbarList3> taskbar;
  HRESULT result = taskbar.CreateInstance(CLSID_TaskbarList, NULL,
    CLSCTX_INPROC_SERVER);

  if (FAILED(result)) {
    error_ = "Failed creating a TaskbarList3 object: ";
    LOG(ERROR) << error_ << result;
    return false;
  }

  result = taskbar->HrInit();
  if (FAILED(result)) {
    error_ = "Failed initializing an ITaskbarList3 interface.";
    LOG(ERROR) << error_;
    return false;
  }

  base::win::ScopedHICON icon;
  HWND hWnd = getHWND(getAppWindow(this));
  if (hWnd == NULL) {
    error_ = kNoAssociatedAppWindow;
    LOG(ERROR) << error_;
    return false;
  }
  const float scale = display::win::GetDPIScale();
  if (badge.size())
    icon = createBadgeIcon(hWnd, base::UTF8ToUTF16(badge).c_str(), 16 * scale, 16 * scale);

  taskbar->SetOverlayIcon(hWnd, icon.get(), L"Status");
#elif defined(OS_LINUX)
  views::LinuxUI* linuxUI = views::LinuxUI::instance();
  if (linuxUI == NULL) {
    error_ = "LinuxUI::instance() is NULL";
    return false;
  }
  SetDeskopEnvironment();
  linuxUI->SetDownloadCount(atoi(badge.c_str()));
#else
  error_ = "NwCurrentWindowInternalSetBadgeLabelFunction NOT Implemented"
  NOTIMPLEMENTED() << error_;
  return false;
#endif
  return true;
}

bool NwCurrentWindowInternalRequestAttentionInternalFunction::RunAsync() {
  EXTENSION_FUNCTION_VALIDATE(args_);
  int count;
  EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &count));
#if defined(OS_WIN)
  FLASHWINFO fwi;
  fwi.cbSize = sizeof(fwi);
  fwi.hwnd = getHWND(getAppWindow(this));
  if (fwi.hwnd == NULL) {
    error_ = kNoAssociatedAppWindow;
    LOG(ERROR) << error_;
    return false;
  }
  if (count != 0) {
    fwi.dwFlags = FLASHW_ALL;
    fwi.uCount = count < 0 ? 4 : count;
    fwi.dwTimeout = 0;
  }
  else {
    fwi.dwFlags = FLASHW_STOP;
  }
  FlashWindowEx(&fwi);
#elif defined(OS_LINUX) || defined(OS_MACOSX)
  AppWindow* window = getAppWindow(this);
  if (!window) {
    error_ = kNoAssociatedAppWindow;
    return false;
  }
  window->GetBaseWindow()->FlashFrame(count);
#endif
  return true;
}

bool NwCurrentWindowInternalSetProgressBarFunction::RunAsync() {
  EXTENSION_FUNCTION_VALIDATE(args_);
  double progress;
  EXTENSION_FUNCTION_VALIDATE(args_->GetDouble(0, &progress));
#if defined(OS_WIN)
  base::win::ScopedComPtr<ITaskbarList3> taskbar;
  HRESULT result = taskbar.CreateInstance(CLSID_TaskbarList, NULL,
    CLSCTX_INPROC_SERVER);

  if (FAILED(result)) {
    error_ = "Failed creating a TaskbarList3 object: ";
    LOG(ERROR) <<  error_ << result;
    return false;
  }

  result = taskbar->HrInit();
  if (FAILED(result)) {
    error_ = "Failed initializing an ITaskbarList3 interface.";
    LOG(ERROR) << error_;
    return false;
  }

  HWND hWnd = getHWND(getAppWindow(this));
  if (hWnd == NULL) {
    error_ = kNoAssociatedAppWindow;
    LOG(ERROR) << error_;
    return false;
  }
  TBPFLAG tbpFlag = TBPF_NOPROGRESS;

  if (progress > 1) {
    tbpFlag = TBPF_INDETERMINATE;
  }
  else if (progress >= 0) {
    tbpFlag = TBPF_NORMAL;
    taskbar->SetProgressValue(hWnd, progress * 100, 100);
  }

  taskbar->SetProgressState(hWnd, tbpFlag);
#elif defined(OS_LINUX)
  views::LinuxUI* linuxUI = views::LinuxUI::instance();
  if (linuxUI == NULL) {
    error_ = "LinuxUI::instance() is NULL";
    return false;
  }
  SetDeskopEnvironment();
  linuxUI->SetProgressFraction(progress);
#else
  error_ = "NwCurrentWindowInternalSetProgressBarFunction NOT Implemented"
  NOTIMPLEMENTED() << error_;
  return false;
#endif
  return true;
}
#endif

bool NwCurrentWindowInternalReloadIgnoringCacheFunction::RunAsync() {
  content::WebContents* web_contents = GetSenderWebContents();
  web_contents->GetController().ReloadBypassingCache(false);
  SendResponse(true);
  return true;
}

bool NwCurrentWindowInternalGetZoomFunction::RunNWSync(base::ListValue* response, std::string* error) {
  content::WebContents* web_contents = GetSenderWebContents();
  if (!web_contents)
    return false;
  double zoom_level =
      ZoomController::FromWebContents(web_contents)->GetZoomLevel();
  response->AppendDouble(zoom_level);
  return true;
}

bool NwCurrentWindowInternalSetZoomFunction::RunNWSync(base::ListValue* response, std::string* error) {
  double zoom_level;

  EXTENSION_FUNCTION_VALIDATE(args_->GetDouble(0, &zoom_level));
  content::WebContents* web_contents = GetSenderWebContents();
  if (!web_contents)
    return false;
  ZoomController* zoom_controller =
      ZoomController::FromWebContents(web_contents);
  scoped_refptr<ExtensionZoomRequestClient> client(
      new ExtensionZoomRequestClient(extension()));
  zoom_controller->SetZoomMode(zoom::ZoomController::ZOOM_MODE_ISOLATED);
  if (!zoom_controller->SetZoomLevelByClient(zoom_level, client)) {
    return false;
  }
  return true;
}

bool NwCurrentWindowInternalEnterKioskModeFunction::RunAsync() {
  AppWindow* window = getAppWindow(this);
  window->ForcedFullscreen();
  SendResponse(true);
  return true;
}

bool NwCurrentWindowInternalLeaveKioskModeFunction::RunAsync() {
  AppWindow* window = getAppWindow(this);
  window->Restore();
  SendResponse(true);
  return true;
}

bool NwCurrentWindowInternalToggleKioskModeFunction::RunAsync() {
  AppWindow* window = getAppWindow(this);
  if (window->IsFullscreen() || window->IsForcedFullscreen())
    window->Restore();
  else
    window->ForcedFullscreen();
  SendResponse(true);
  return true;
}

bool NwCurrentWindowInternalIsKioskInternalFunction::RunNWSync(base::ListValue* response, std::string* error) {
  AppWindow* window = getAppWindow(this);
  if (window->IsFullscreen() || window->IsForcedFullscreen())
    response->AppendBoolean(true);
  else
    response->AppendBoolean(false);
  return true;
}

bool NwCurrentWindowInternalGetTitleInternalFunction::RunNWSync(base::ListValue* response, std::string* error) {
  AppWindow* window = getAppWindow(this);
  response->AppendString(window->title_override());
  return true;
}

bool NwCurrentWindowInternalSetTitleInternalFunction::RunNWSync(base::ListValue* response, std::string* error) {
  EXTENSION_FUNCTION_VALIDATE(args_);
  std::string title;
  EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &title));
  AppWindow* window = getAppWindow(this);
  window->set_title_override(title);
  window->GetBaseWindow()->UpdateWindowTitle();
  return true;
}

bool NwCurrentWindowInternalGetPrintersFunction::RunAsync() {
  printing::EnumeratePrinters(Profile::FromBrowserContext(browser_context()),
      base::Bind(&NwCurrentWindowInternalGetPrintersFunction::OnGetPrinterList,
                 this));
  return true;
}

void NwCurrentWindowInternalGetPrintersFunction::OnGetPrinterList(const printing::PrinterList& printer_list) {
  base::ListValue* printers = new base::ListValue();
  chrome::PrintersToValues(printer_list, printers);
  SetResult(base::WrapUnique(printers));
  SendResponse(true);
}

bool NwCurrentWindowInternalSetPrintSettingsInternalFunction::RunNWSync(base::ListValue* response, std::string* error) {
  EXTENSION_FUNCTION_VALIDATE(args_);

  if (!args_->GetSize())
    return false;
  base::Value* spec = NULL;
  EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &spec) && spec);
  if (!spec->IsType(base::Value::TYPE_DICTIONARY))
    return false;
  const base::DictionaryValue* dict = static_cast<const base::DictionaryValue*>(spec);
  bool auto_print;
  std::string printer_name, pdf_path;
  if (dict->GetBoolean("autoprint", &auto_print))
    chrome::NWPrintSetCustomPrinting(auto_print);
  if (dict->GetString("printer", &printer_name))
    chrome::NWPrintSetDefaultPrinter(printer_name);
  if (dict->GetString("pdf_path", &pdf_path))
    chrome::NWPrintSetPDFPath(base::FilePath::FromUTF8Unsafe(pdf_path));
  chrome::NWPrintSetOptions(dict);
  return true;
}

bool NwCurrentWindowInternalGetWinParamInternalFunction::RunNWSync(base::ListValue* response, std::string* error) {
  AppWindow* app_window = getAppWindow(this);

  if (!app_window) {
    *error = "cannot get current window; are you in background page/node context?";
    return false;
  }
  //from app_window_api.cc
  content::RenderFrameHost* created_frame =
      app_window->web_contents()->GetMainFrame();
  int frame_id = created_frame->GetRoutingID();

  base::DictionaryValue* result = new base::DictionaryValue;
  result->Set("frameId", new base::FundamentalValue(frame_id));
  result->Set("id", new base::StringValue(app_window->window_key()));
  app_window->GetSerializedState(result);

  response->Append(base::WrapUnique(result));

  return true;
}

bool NwCurrentWindowInternalSetShowInTaskbarFunction::RunAsync() {
  EXTENSION_FUNCTION_VALIDATE(args_);
  bool show;
  EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(0, &show));
#if defined(OS_WIN)
  AppWindow* window = getAppWindow(this);

  native_app_window::NativeAppWindowViews* native_app_window_views =
      static_cast<native_app_window::NativeAppWindowViews*>(
          window->GetBaseWindow());

  views::Widget* widget = native_app_window_views->widget()->GetTopLevelWidget();

  if (show == false && base::win::GetVersion() < base::win::VERSION_VISTA) {
    // Change the owner of native window. Only needed on Windows XP.
    ::SetParent(views::HWNDForWidget(widget),
                ui::GetHiddenWindow());
  }

  base::win::ScopedComPtr<ITaskbarList> taskbar;
  HRESULT result = taskbar.CreateInstance(CLSID_TaskbarList, NULL,
                                          CLSCTX_INPROC_SERVER);
  if (FAILED(result)) {
    VLOG(1) << "Failed creating a TaskbarList object: " << result;
    SendResponse(true);
    return true;
  }

  result = taskbar->HrInit();
  if (FAILED(result)) {
    LOG(ERROR) << "Failed initializing an ITaskbarList interface.";
    SendResponse(true);
    return true;
  }

  if (show)
    result = taskbar->AddTab(views::HWNDForWidget(widget));
  else
    result = taskbar->DeleteTab(views::HWNDForWidget(widget));

  if (FAILED(result)) {
    LOG(ERROR) << "Failed to change the show in taskbar attribute";
    SendResponse(true);
    return true;
  }
#elif defined(OS_MACOSX)
  AppWindow* app_window = getAppWindow(this);
  extensions::NativeAppWindow* native_window = app_window->GetBaseWindow();
  NWSetNSWindowShowInTaskbar(native_window, show);
#endif
  SendResponse(true);
  return true;
}

} // namespace extensions


// Copyright (c) 2012 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.

#ifndef NW_APP_PROTOCOL_HANDLER_H_
#define NW_APP_PROTOCOL_HANDLER_H_

#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/files/file_path.h"
#include "net/url_request/url_request_job_factory.h"

class GURL;

namespace net {

class NetworkDelegate;
class URLRequestJob;

// Implements a ProtocolHandler for File jobs. If |network_delegate_| is NULL,
// then all file requests will fail with ERR_ACCESS_DENIED.
class AppProtocolHandler :
    public URLRequestJobFactory::ProtocolHandler {
 public:
  AppProtocolHandler(const base::FilePath& root);
  URLRequestJob* MaybeCreateJob(
      URLRequest* request, NetworkDelegate* network_delegate) const override;
  bool IsSafeRedirectTarget(const GURL& location) const override;

 private:
  base::FilePath root_path_;
  DISALLOW_COPY_AND_ASSIGN(AppProtocolHandler);
};

}  // namespace net

#endif


This product links to certain Linux system libraries licensed under LGPL.


// Copyright (c) 2012 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 "content/nw/src/browser/printing/print_view_manager.h"

#include <map>

#include "base/bind.h"
#include "base/command_line.h"
#include "base/lazy_instance.h"
#include "base/memory/scoped_ptr.h"
#include "base/metrics/histogram.h"
#include "base/prefs/pref_service.h"
#include "base/timer/timer.h"
#include "base/strings/utf_string_conversions.h"
//#include "chrome/browser/browser_process.h"
//#include "chrome/browser/printing/print_error_dialog.h"
#include "content/nw/src/browser/printing/print_job.h"
#include "content/nw/src/browser/printing/print_job_manager.h"
//#include "content/nw/src/browser/printing/print_preview_dialog_controller.h"
#include "content/nw/src/browser/printing/print_view_manager_observer.h"
#include "content/nw/src/browser/printing/printer_query.h"
//#include "chrome/browser/profiles/profile.h"
//#include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
#include "content/nw/src/shell_content_browser_client.h"
#include "content/public/browser/notification_types.h"
//#include "chrome/common/chrome_switches.h"
//#include "chrome/common/pref_names.h"
#include "content/nw/src/common/print_messages.h"
#include "content/nw/src/common/shell_switches.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "grit/nw_resources.h"
#include "printing/metafile.h"
#include "printing/metafile_impl.h"
#include "printing/print_destination_interface.h"
#include "printing/printed_document.h"
#include "ui/base/l10n/l10n_util.h"

using base::TimeDelta;
using base::MessageLoop;
using content::BrowserThread;
using content::WebContents;

DEFINE_WEB_CONTENTS_USER_DATA_KEY(printing::PrintViewManager);

namespace {

#if defined(OS_WIN) && !defined(WIN_PDF_METAFILE_FOR_PRINTING)
// Limits memory usage by raster to 64 MiB.
const int kMaxRasterSizeInPixels = 16*1024*1024;
#endif

}  // namespace

namespace printing {

PrintViewManager::PrintViewManager(content::WebContents* web_contents)
    : content::WebContentsObserver(web_contents),
      number_pages_(0),
      printing_succeeded_(false),
      inside_inner_message_loop_(false),
      observer_(NULL),
      cookie_(0),
      scripted_print_preview_rph_(NULL),
      tab_content_blocked_(false) {
#if defined(OS_POSIX) && !defined(OS_MACOSX)
  expecting_first_page_ = true;
#endif
  registrar_.Add(this, content::NOTIFICATION_CONTENT_BLOCKED_STATE_CHANGED,
                 content::Source<content::WebContents>(web_contents));
  printing_enabled_ = true;
}

PrintViewManager::~PrintViewManager() {
  ReleasePrinterQuery();
  DisconnectFromCurrentPrintJob();
}

bool PrintViewManager::PrintNow() {
  return PrintNowInternal(new PrintMsg_PrintPages(routing_id()));
}

bool PrintViewManager::PrintForSystemDialogNow() {
  return PrintNowInternal(new PrintMsg_PrintForSystemDialog(routing_id()));
}

// bool PrintViewManager::AdvancedPrintNow() {
//   return PrintNow();
// }

bool PrintViewManager::PrintToDestination() {
  // TODO(mad): Remove this once we can send user metrics from the metro driver.
  // crbug.com/142330
  UMA_HISTOGRAM_ENUMERATION("Metro.Print", 0, 2);
  // TODO(mad): Use a passed in destination interface instead.
  content::ShellContentBrowserClient* browser_client =
    static_cast<content::ShellContentBrowserClient*>(content::GetContentClient()->browser());
    browser_client->print_job_manager()->SetPrintDestination(
      printing::CreatePrintDestination());
  return PrintNowInternal(new PrintMsg_PrintPages(routing_id()));
}

void PrintViewManager::UpdateScriptedPrintingBlocked() {
  Send(new PrintMsg_SetScriptedPrintingBlocked(
       routing_id(),
       !printing_enabled_ || tab_content_blocked_));
}

void PrintViewManager::set_observer(PrintViewManagerObserver* observer) {
  DCHECK(!observer || !observer_);
  observer_ = observer;
}

void PrintViewManager::NavigationStopped() {
  // Cancel the current job, wait for the worker to finish.
  TerminatePrintJob(true);
}

void PrintViewManager::RenderProcessGone(base::TerminationStatus status) {
  ReleasePrinterQuery();

  if (!print_job_.get())
    return;

  scoped_refptr<PrintedDocument> document(print_job_->document());
  if (document) {
    // If IsComplete() returns false, the document isn't completely rendered.
    // Since our renderer is gone, there's nothing to do, cancel it. Otherwise,
    // the print job may finish without problem.
    TerminatePrintJob(!document->IsComplete());
  }
}

base::string16 PrintViewManager::RenderSourceName() {
  base::string16 name(web_contents()->GetTitle());
  if (name.empty())
    name = l10n_util::GetStringUTF16(IDS_DEFAULT_PRINT_DOCUMENT_TITLE);
  return name;
}

void PrintViewManager::OnDidGetPrintedPagesCount(int cookie, int number_pages) {
  DCHECK_GT(cookie, 0);
  DCHECK_GT(number_pages, 0);
  number_pages_ = number_pages;
  OpportunisticallyCreatePrintJob(cookie);
}

void PrintViewManager::OnDidGetDocumentCookie(int cookie) {
  cookie_ = cookie;
}

void PrintViewManager::OnDidShowPrintDialog() {
  if (observer_)
    observer_->OnPrintDialogShown();
}

void PrintViewManager::OnDidPrintPage(
    const PrintHostMsg_DidPrintPage_Params& params) {
  if (!OpportunisticallyCreatePrintJob(params.document_cookie))
    return;

  PrintedDocument* document = print_job_->document();
  if (!document || params.document_cookie != document->cookie()) {
    // Out of sync. It may happen since we are completely asynchronous. Old
    // spurious messages can be received if one of the processes is overloaded.
    return;
  }

#if defined(OS_WIN) || defined(OS_MACOSX)
  const bool metafile_must_be_valid = true;
#elif defined(OS_POSIX)
  const bool metafile_must_be_valid = expecting_first_page_;
  expecting_first_page_ = false;
#endif

  base::SharedMemory shared_buf(params.metafile_data_handle, true);
  if (metafile_must_be_valid) {
    if (!shared_buf.Map(params.data_size)) {
      NOTREACHED() << "couldn't map";
      web_contents()->Stop();
      return;
    }
  }

  scoped_ptr<NativeMetafile> metafile(new NativeMetafile);
  if (metafile_must_be_valid) {
    if (!metafile->InitFromData(shared_buf.memory(), params.data_size)) {
      NOTREACHED() << "Invalid metafile header";
      web_contents()->Stop();
      return;
    }
  }

#if defined(OS_WIN) && !defined(WIN_PDF_METAFILE_FOR_PRINTING)
  bool big_emf = (params.data_size && params.data_size >= kMetafileMaxSize);
  const CommandLine* cmdline = CommandLine::ForCurrentProcess();
  int raster_size = std::min(params.page_size.GetArea(),
                             kMaxRasterSizeInPixels);
  if (big_emf) {
    scoped_ptr<NativeMetafile> raster_metafile(
        metafile->RasterizeMetafile(raster_size));
    if (raster_metafile.get()) {
      metafile.swap(raster_metafile);
    } else if (big_emf) {
      // Don't fall back to emf here.
      NOTREACHED() << "size:" << params.data_size;
      TerminatePrintJob(true);
      web_contents()->Stop();
      return;
    }
  }
#endif

  // Update the rendered document. It will send notifications to the listener.
  document->SetPage(params.page_number,
                    metafile.release(),
#if defined(OS_WIN)
                    params.actual_shrink,
#endif
                    params.page_size,
                    params.content_area);

  ShouldQuitFromInnerMessageLoop();
}

void PrintViewManager::OnPrintingFailed(int cookie) {
  if (cookie != cookie_) {
    NOTREACHED();
    return;
  }

  ReleasePrinterQuery();

  content::NotificationService::current()->Notify(
      content::NOTIFICATION_PRINT_JOB_RELEASED,
      content::Source<content::WebContents>(web_contents()),
      content::NotificationService::NoDetails());
}

void PrintViewManager::OnScriptedPrintPreview(bool source_is_modifiable,
                                              IPC::Message* reply_msg) {
}

void PrintViewManager::OnScriptedPrintPreviewReply(IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  Send(reply_msg);
}

void PrintViewManager::DidStartLoading(
    content::RenderViewHost* render_view_host) {
  UpdateScriptedPrintingBlocked();
}

bool PrintViewManager::OnMessageReceived(const IPC::Message& message) {
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP(PrintViewManager, message)
    IPC_MESSAGE_HANDLER(PrintHostMsg_DidGetPrintedPagesCount,
                        OnDidGetPrintedPagesCount)
    IPC_MESSAGE_HANDLER(PrintHostMsg_DidGetDocumentCookie,
                        OnDidGetDocumentCookie)
    IPC_MESSAGE_HANDLER(PrintHostMsg_DidShowPrintDialog, OnDidShowPrintDialog)
    IPC_MESSAGE_HANDLER(PrintHostMsg_DidPrintPage, OnDidPrintPage)
    IPC_MESSAGE_HANDLER(PrintHostMsg_PrintingFailed, OnPrintingFailed)
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP()
  return handled;
}

void PrintViewManager::Observe(int type,
                               const content::NotificationSource& source,
                               const content::NotificationDetails& details) {
  switch (type) {
    case content::NOTIFICATION_PRINT_JOB_EVENT: {
      OnNotifyPrintJobEvent(*content::Details<JobEventDetails>(details).ptr());
      break;
    }
    case content::NOTIFICATION_CONTENT_BLOCKED_STATE_CHANGED: {
      tab_content_blocked_ = *content::Details<const bool>(details).ptr();
      UpdateScriptedPrintingBlocked();
      break;
    }
    default: {
      NOTREACHED();
      break;
    }
  }
}

void PrintViewManager::OnNotifyPrintJobEvent(
    const JobEventDetails& event_details) {
  switch (event_details.type()) {
    case JobEventDetails::FAILED: {
      TerminatePrintJob(true);

      content::NotificationService::current()->Notify(
          content::NOTIFICATION_PRINT_JOB_RELEASED,
          content::Source<content::WebContents>(web_contents()),
          content::NotificationService::NoDetails());
      break;
    }
    case JobEventDetails::USER_INIT_DONE:
    case JobEventDetails::DEFAULT_INIT_DONE:
    case JobEventDetails::USER_INIT_CANCELED: {
      NOTREACHED();
      break;
    }
    case JobEventDetails::ALL_PAGES_REQUESTED: {
      ShouldQuitFromInnerMessageLoop();
      break;
    }
    case JobEventDetails::NEW_DOC:
    case JobEventDetails::NEW_PAGE:
    case JobEventDetails::PAGE_DONE:
    case JobEventDetails::DOC_DONE: {
      // Don't care about the actual printing process.
      break;
    }
    case JobEventDetails::JOB_DONE: {
      // Printing is done, we don't need it anymore.
      // print_job_->is_job_pending() may still be true, depending on the order
      // of object registration.
      printing_succeeded_ = true;
      ReleasePrintJob();

      content::NotificationService::current()->Notify(
          content::NOTIFICATION_PRINT_JOB_RELEASED,
          content::Source<content::WebContents>(web_contents()),
          content::NotificationService::NoDetails());
      break;
    }
    default: {
      NOTREACHED();
      break;
    }
  }
}

bool PrintViewManager::RenderAllMissingPagesNow() {
  if (!print_job_.get() || !print_job_->is_job_pending())
    return false;

  // We can't print if there is no renderer.
  if (!web_contents() ||
      !web_contents()->GetRenderViewHost() ||
      !web_contents()->GetRenderViewHost()->IsRenderViewLive()) {
    return false;
  }

  // Is the document already complete?
  if (print_job_->document() && print_job_->document()->IsComplete()) {
    printing_succeeded_ = true;
    return true;
  }

  // WebContents is either dying or a second consecutive request to print
  // happened before the first had time to finish. We need to render all the
  // pages in an hurry if a print_job_ is still pending. No need to wait for it
  // to actually spool the pages, only to have the renderer generate them. Run
  // a message loop until we get our signal that the print job is satisfied.
  // PrintJob will send a ALL_PAGES_REQUESTED after having received all the
  // pages it needs. MessageLoop::current()->Quit() will be called as soon as
  // print_job_->document()->IsComplete() is true on either ALL_PAGES_REQUESTED
  // or in DidPrintPage(). The check is done in
  // ShouldQuitFromInnerMessageLoop().
  // BLOCKS until all the pages are received. (Need to enable recursive task)
  if (!RunInnerMessageLoop()) {
    // This function is always called from DisconnectFromCurrentPrintJob() so we
    // know that the job will be stopped/canceled in any case.
    return false;
  }
  return true;
}

void PrintViewManager::ShouldQuitFromInnerMessageLoop() {
  // Look at the reason.
  DCHECK(print_job_->document());
  if (print_job_->document() &&
      print_job_->document()->IsComplete() &&
      inside_inner_message_loop_) {
    // We are in a message loop created by RenderAllMissingPagesNow. Quit from
    // it.
    MessageLoop::current()->Quit();
    inside_inner_message_loop_ = false;
  }
}

bool PrintViewManager::CreateNewPrintJob(PrintJobWorkerOwner* job) {
  DCHECK(!inside_inner_message_loop_);

  // Disconnect the current print_job_.
  DisconnectFromCurrentPrintJob();

  // We can't print if there is no renderer.
  if (!web_contents()->GetRenderViewHost() ||
      !web_contents()->GetRenderViewHost()->IsRenderViewLive()) {
    return false;
  }

  // Ask the renderer to generate the print preview, create the print preview
  // view and switch to it, initialize the printer and show the print dialog.
  DCHECK(!print_job_.get());
  DCHECK(job);
  if (!job)
    return false;

  print_job_ = new PrintJob();
  print_job_->Initialize(job, this, number_pages_);
  registrar_.Add(this, content::NOTIFICATION_PRINT_JOB_EVENT,
                 content::Source<PrintJob>(print_job_.get()));
  printing_succeeded_ = false;
  return true;
}

void PrintViewManager::DisconnectFromCurrentPrintJob() {
  // Make sure all the necessary rendered page are done. Don't bother with the
  // return value.
  bool result = RenderAllMissingPagesNow();

  // Verify that assertion.
  if (print_job_.get() &&
      print_job_->document() &&
      !print_job_->document()->IsComplete()) {
    DCHECK(!result);
    // That failed.
    TerminatePrintJob(true);
  } else {
    // DO NOT wait for the job to finish.
    ReleasePrintJob();
  }
#if defined(OS_POSIX) && !defined(OS_MACOSX)
  expecting_first_page_ = true;
#endif
}

void PrintViewManager::PrintingDone(bool success) {
  if (!print_job_.get())
    return;
  Send(new PrintMsg_PrintingDone(routing_id(), success));
}

void PrintViewManager::TerminatePrintJob(bool cancel) {
  if (!print_job_.get())
    return;

  if (cancel) {
    // We don't need the metafile data anymore because the printing is canceled.
    print_job_->Cancel();
    inside_inner_message_loop_ = false;
  } else {
    DCHECK(!inside_inner_message_loop_);
    DCHECK(!print_job_->document() || print_job_->document()->IsComplete());

    // WebContents is either dying or navigating elsewhere. We need to render
    // all the pages in an hurry if a print job is still pending. This does the
    // trick since it runs a blocking message loop:
    print_job_->Stop();
  }
  ReleasePrintJob();
}

void PrintViewManager::ReleasePrintJob() {
  if (!print_job_.get())
    return;

  PrintingDone(printing_succeeded_);

  registrar_.Remove(this, content::NOTIFICATION_PRINT_JOB_EVENT,
                    content::Source<PrintJob>(print_job_.get()));
  print_job_->DisconnectSource();
  // Don't close the worker thread.
  print_job_ = NULL;
}

bool PrintViewManager::RunInnerMessageLoop() {
  // This value may actually be too low:
  //
  // - If we're looping because of printer settings initialization, the premise
  // here is that some poor users have their print server away on a VPN over a
  // slow connection. In this situation, the simple fact of opening the printer
  // can be dead slow. On the other side, we don't want to die infinitely for a
  // real network error. Give the printer 60 seconds to comply.
  //
  // - If we're looping because of renderer page generation, the renderer could
  // be CPU bound, the page overly complex/large or the system just
  // memory-bound.
  static const int kPrinterSettingsTimeout = 60000;
  base::OneShotTimer<MessageLoop> quit_timer;
  quit_timer.Start(FROM_HERE,
                   TimeDelta::FromMilliseconds(kPrinterSettingsTimeout),
                   MessageLoop::current(), &MessageLoop::Quit);

  inside_inner_message_loop_ = true;

  // Need to enable recursive task.
  {
    MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current());
    MessageLoop::current()->Run();
  }

  bool success = true;
  if (inside_inner_message_loop_) {
    // Ok we timed out. That's sad.
    inside_inner_message_loop_ = false;
    success = false;
  }

  return success;
}

bool PrintViewManager::OpportunisticallyCreatePrintJob(int cookie) {
  if (print_job_.get())
    return true;

  if (!cookie) {
    // Out of sync. It may happens since we are completely asynchronous. Old
    // spurious message can happen if one of the processes is overloaded.
    return false;
  }

  // The job was initiated by a script. Time to get the corresponding worker
  // thread.
  scoped_refptr<PrinterQuery> queued_query;
  content::ShellContentBrowserClient* browser_client =
    static_cast<content::ShellContentBrowserClient*>(content::GetContentClient()->browser());
  browser_client->print_job_manager()->PopPrinterQuery(cookie, &queued_query);
  DCHECK(queued_query.get());
  if (!queued_query.get())
    return false;

  if (!CreateNewPrintJob(queued_query.get())) {
    // Don't kill anything.
    return false;
  }

  // Settings are already loaded. Go ahead. This will set
  // print_job_->is_job_pending() to true.
  print_job_->StartPrinting();
  return true;
}

bool PrintViewManager::PrintNowInternal(IPC::Message* message) {
  // Don't print / print preview interstitials.
  if (web_contents()->ShowingInterstitialPage()) {
    delete message;
    return false;
  }
  return Send(message);
}

void PrintViewManager::ReleasePrinterQuery() {
  if (!cookie_)
    return;

  int cookie = cookie_;
  cookie_ = 0;
  content::ShellContentBrowserClient* browser_client =
    static_cast<content::ShellContentBrowserClient*>(content::GetContentClient()->browser());
  browser_client->print_job_manager()->SetPrintDestination(NULL);

  printing::PrintJobManager* print_job_manager =
      browser_client->print_job_manager();
  // May be NULL in tests.
  if (!print_job_manager)
    return;

  scoped_refptr<printing::PrinterQuery> printer_query;
  print_job_manager->PopPrinterQuery(cookie, &printer_query);
  if (!printer_query.get())
    return;
  BrowserThread::PostTask(
      BrowserThread::IO, FROM_HERE,
      base::Bind(&PrinterQuery::StopWorker, printer_query.get()));
}

}  // namespace printing


#if defined(OS_WIN) || defined(OS_LINUX)

bool MenuItem::AcceleratorPressed(const ui::Accelerator& accelerator) {

keep keep keep keep keep replace replace replace replace replace replace replace replace replace replace replace replace keep keep keep


function mytest(a) {
  document.write("
" + (a + 42) + "
");
}


// Returns a wrapper function that returns a wrapped callback
// The wrapper function should do some stuff, and return a
// presumably different callback function.
// This makes sure that own properties are retained, so that
// decorations and such are not lost along the way.
module.exports = wrappy
function wrappy (fn, cb) {
  if (fn && cb) return wrappy(fn)(cb)

  if (typeof fn !== 'function')
    throw new TypeError('need wrapper function')

  Object.keys(fn).forEach(function (k) {
    wrapper[k] = fn[k]
  })

  return wrapper

  function wrapper() {
    var args = new Array(arguments.length)
    for (var i = 0; i < args.length; i++) {
      args[i] = arguments[i]
    }
    var ret = fn.apply(this, args)
    var cb = args[args.length-1]
    if (typeof ret === 'function' && ret !== cb) {
      Object.keys(cb).forEach(function (k) {
        ret[k] = cb[k]
      })
    }
    return ret
  }
}


<!DOCTYPE html>

  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>Screen</title>


keep keep keep keep replace replace keep keep keep keep


#import "content/nw/src/api/base/base_mac.h"
#import <objc/runtime.h>

@interface CppWrapper : NSObject {
  void* _obj;
}
+ (id) createFromCppObject:(void*) obj;
- (id) initWithCppObject:(void*) obj;
- (void*) obj;
@end

@implementation CppWrapper

+ (id) createFromCppObject:(void*) obj {
  return [[CppWrapper alloc] initWithCppObject:obj];
}

- (id) initWithCppObject:(void*) obj {
  _obj = obj;
  return self;
}

- (void*) obj {
  return _obj;
}

@end

@implementation NSObject (AssociatedObject)
@dynamic associatedObject;

- (void)setAssociatedObject:(void*)object {
  objc_setAssociatedObject(self, @selector(associatedObject), [CppWrapper createFromCppObject:object], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (void*)associatedObject {
  CppWrapper* wrapper = objc_getAssociatedObject(self, @selector(associatedObject));
  return wrapper == nil ? nil : [wrapper obj];
}

@end


<!DOCTYPE html>

	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<title>localstorage</title>

localstorage migration test


nw::ObjectManager* obj_manager = nw::ObjectManager::Get(browser_context());

Menu* menu = (Menu*)obj_manager->GetApiObject(id);

keep keep keep keep keep replace keep keep keep keep


// Copyright (c) 2013 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 "content/nw/src/browser/nw_form_database_service.h"
#include "base/logging.h"
#include "base/synchronization/waitable_event.h"
#include "components/autofill/core/browser/webdata/autofill_table.h"
#include "components/webdata/common/webdata_constants.h"
#include "content/public/browser/browser_thread.h"
//#include "ui/base/l10n/l10n_util.h"

using base::WaitableEvent;
using content::BrowserThread;

namespace {

// Callback to handle database error. It seems chrome uses this to
// display an error dialog box only.
void DatabaseErrorCallback(sql::InitStatus status) {
  LOG(WARNING) << "initializing autocomplete database failed";
}

}  // namespace

namespace nw {

NwFormDatabaseService::NwFormDatabaseService(const base::FilePath path) {
  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  web_database_ = new WebDatabaseService(path.Append(kWebDataFilename),
      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI),
      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB));
  web_database_->AddTable(
      scoped_ptr<WebDatabaseTable>(new autofill::AutofillTable(
          "")));
  web_database_->LoadDatabase();

  autofill_data_ = new autofill::AutofillWebDataService(
      web_database_,
      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI),
      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB),
      base::Bind(&DatabaseErrorCallback));
  autofill_data_->Init();
}

NwFormDatabaseService::~NwFormDatabaseService() {
  Shutdown();
}

void NwFormDatabaseService::Shutdown() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK(result_map_.empty());
  // TODO(sgurun) we don't run into this logic right now,
  // but if we do, then we need to implement cancellation
  // of pending queries.
  autofill_data_->ShutdownOnUIThread();
  web_database_->ShutdownDatabase();
}

scoped_refptr<autofill::AutofillWebDataService>
NwFormDatabaseService::get_autofill_webdata_service() {
  return autofill_data_;
}

void NwFormDatabaseService::ClearFormData() {
  BrowserThread::PostTask(
      BrowserThread::DB,
      FROM_HERE,
      base::Bind(&NwFormDatabaseService::ClearFormDataImpl,
                 base::Unretained(this)));
}

void NwFormDatabaseService::ClearFormDataImpl() {
  base::Time begin;
  base::Time end = base::Time::Max();
  autofill_data_->RemoveFormElementsAddedBetween(begin, end);
  autofill_data_->RemoveAutofillDataModifiedBetween(begin, end);
}

bool NwFormDatabaseService::HasFormData() {
  WaitableEvent completion(false, false);
  bool result = false;
  BrowserThread::PostTask(
      BrowserThread::DB,
      FROM_HERE,
      base::Bind(&NwFormDatabaseService::HasFormDataImpl,
                 base::Unretained(this),
                 &completion,
                 &result));
  completion.Wait();
  return result;
}

void NwFormDatabaseService::HasFormDataImpl(
    WaitableEvent* completion,
    bool* result) {
  WebDataServiceBase::Handle pending_query_handle =
      autofill_data_->HasFormElements(this);
  PendingQuery query;
  query.result = result;
  query.completion = completion;
  result_map_[pending_query_handle] = query;
}

void NwFormDatabaseService::OnWebDataServiceRequestDone(
    WebDataServiceBase::Handle h,
    const WDTypedResult* result) {

  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
  bool has_form_data = false;
  if (result) {
    DCHECK_EQ(AUTOFILL_VALUE_RESULT, result->GetType());
    const WDResult<bool>* autofill_result =
        static_cast<const WDResult<bool>*>(result);
    has_form_data = autofill_result->GetValue();
  }
  QueryMap::const_iterator it = result_map_.find(h);
  if (it == result_map_.end()) {
    LOG(WARNING) << "Received unexpected callback from web data service";
    return;
  }
  *(it->second.result) = has_form_data;
  it->second.completion->Signal();
  result_map_.erase(h);
}

}  // namespace android_webview


import time
import os
import subprocess
import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from nw_util import *

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common import utils
from selenium.common.exceptions import NoSuchElementException

chrome_options = Options()
chrome_options.add_argument("nwapp=" + os.path.dirname(os.path.abspath(__file__)))

capabilities = {"pageLoadStrategy": "none"}

testdir = os.path.dirname(os.path.abspath(__file__))
os.chdir(testdir)

port = str(utils.free_port())
server = subprocess.Popen(['python', 'http-server.py', port])

html = open('index.html', 'w')
html.write('''

''' % (port))

html.close()

run_counter = 6

try:
    while run_counter > 0 :
        run_counter = run_counter - 1
        driver = webdriver.Chrome(executable_path=os.environ['CHROMEDRIVER'], chrome_options=chrome_options, desired_capabilities = capabilities)
        time.sleep(1)
        try:
            driver.switch_to_window("main")
            print driver.current_url
            result = ''
            wait_counter = 10
            while wait_counter > 0 :
                try:
                    wait_counter = wait_counter - 1
                    time.sleep(1)
                    result = driver.find_element_by_id('res').get_attribute('innerHTML')
                    break
                except NoSuchElementException:
                    pass
            print result
            assert("object" in result)
        finally:
            driver.quit()

finally:
    server.terminate()


#ifndef NW_API_APP_API_H_
#define NW_API_APP_API_H_

#include <vector>

#include "base/run_loop.h"
#include "chrome/browser/browsing_data/browsing_data_remover.h"
#include "extensions/browser/extension_function.h"

namespace extensions {

class NwAppQuitFunction : public AsyncExtensionFunction {
 public:
  NwAppQuitFunction();

 protected:
  ~NwAppQuitFunction() override;

  // ExtensionFunction:
  bool RunAsync() override;
  DECLARE_EXTENSION_FUNCTION("nw.App.quit", UNKNOWN)
 private:
  void Callback();
};

class NwAppCloseAllWindowsFunction : public AsyncExtensionFunction {
 public:
  NwAppCloseAllWindowsFunction() {}

 protected:
  ~NwAppCloseAllWindowsFunction() override {}

  // ExtensionFunction:
  bool RunAsync() override;
  DECLARE_EXTENSION_FUNCTION("nw.App.closeAllWindows", UNKNOWN)
};

class NwAppGetArgvSyncFunction : public NWSyncExtensionFunction {
 public:
  NwAppGetArgvSyncFunction();
  bool RunNWSync(base::ListValue* response, std::string* error) override;

 protected:
  ~NwAppGetArgvSyncFunction() override;

  DECLARE_EXTENSION_FUNCTION("nw.App.getArgvSync", UNKNOWN)
 private:
  DISALLOW_COPY_AND_ASSIGN(NwAppGetArgvSyncFunction);
};

class NwAppClearCacheFunction : public NWSyncExtensionFunction, public BrowsingDataRemover::Observer {
 public:
  NwAppClearCacheFunction();
  bool RunNWSync(base::ListValue* response, std::string* error) override;
  void OnBrowsingDataRemoverDone() override;

 protected:
  ~NwAppClearCacheFunction() override;

  base::RunLoop run_loop_;

  DECLARE_EXTENSION_FUNCTION("nw.App.clearCache", UNKNOWN)
 private:
  DISALLOW_COPY_AND_ASSIGN(NwAppClearCacheFunction);
};

class NwAppClearAppCacheFunction : public NWSyncExtensionFunction {
 public:
  NwAppClearAppCacheFunction();
  bool RunNWSync(base::ListValue* response, std::string* error) override;

 protected:
  ~NwAppClearAppCacheFunction() override;

  DECLARE_EXTENSION_FUNCTION("nw.App.clearAppCache", UNKNOWN)
 private:
  DISALLOW_COPY_AND_ASSIGN(NwAppClearAppCacheFunction);
};

class NwAppSetProxyConfigFunction : public NWSyncExtensionFunction {
 public:
  NwAppSetProxyConfigFunction();
  bool RunNWSync(base::ListValue* response, std::string* error) override;

 protected:
  ~NwAppSetProxyConfigFunction() override;

  DECLARE_EXTENSION_FUNCTION("nw.App.setProxyConfig", UNKNOWN)
 private:
  DISALLOW_COPY_AND_ASSIGN(NwAppSetProxyConfigFunction);
};

class NwAppGetDataPathFunction : public NWSyncExtensionFunction {
 public:
  NwAppGetDataPathFunction(){}
  bool RunNWSync(base::ListValue* response, std::string* error) override;

 protected:
  ~NwAppGetDataPathFunction() override {}

  DECLARE_EXTENSION_FUNCTION("nw.App.getDataPath", UNKNOWN)
 private:
  DISALLOW_COPY_AND_ASSIGN(NwAppGetDataPathFunction);
};

class NwAppCrashBrowserFunction : public AsyncExtensionFunction {
 public:
  NwAppCrashBrowserFunction() {}

 protected:
  ~NwAppCrashBrowserFunction() override {}

  // ExtensionFunction:
  bool RunAsync() override;
  DECLARE_EXTENSION_FUNCTION("nw.App.crashBrowser", UNKNOWN)
};

} // namespace extensions
#endif


keep keep keep replace keep keep keep keep keep


# Contributors of Documents {: .doctitle}
---

* Roger Wang <roger.wang@intel.com>
* Cong Liu <cong.liu@intel.com>

docs/For Developers/Contributors of Documents.md


#ifndef NW_SRC_API_NW_SHORTCUT_API_H
#define NW_SRC_API_NW_SHORTCUT_API_H

#include "extensions/browser/extension_function.h"

namespace extensions {

  class NwShortcutRegisterAcceleratorFunction : public NWSyncExtensionFunction
  {
  public:
    NwShortcutRegisterAcceleratorFunction() {}
    bool RunNWSync(base::ListValue* response, std::string* error) override;

  protected:
    ~NwShortcutRegisterAcceleratorFunction() override {}
    DECLARE_EXTENSION_FUNCTION("nw.Shortcut.registerAccelerator", UNKNOWN)

  private:
    DISALLOW_COPY_AND_ASSIGN(NwShortcutRegisterAcceleratorFunction);
  };

  class NwShortcutUnregisterAcceleratorFunction : public NWSyncExtensionFunction
  {
  public:
    NwShortcutUnregisterAcceleratorFunction() {}
    bool RunNWSync(base::ListValue* response, std::string* error) override;

  protected:
    ~NwShortcutUnregisterAcceleratorFunction() override {}
    DECLARE_EXTENSION_FUNCTION("nw.Shortcut.unregisterAccelerator", UNKNOWN)

  private:
    DISALLOW_COPY_AND_ASSIGN(NwShortcutUnregisterAcceleratorFunction);
  };

} // extensions

#endif // NW_SRC_API_NW_SHORTCUT_API_H


<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="pl">
<translation id="9065203028668620118">Edycja</translation>
<translation id="5230516054153933099">Okno</translation>
<translation id="7772127298218883077"><ph name="PRODUCT_NAME"/> – informacje</translation>
<translation id="3315158641124845231">Ukryj <ph name="PRODUCT_NAME"/></translation>
<translation id="881799181680267069">Ukryj pozostałe</translation>
<translation id="923467487918828349">Pokaż wszystko</translation>
<translation id="4708849949179781599">Zamknij przeglądarkę <ph name="PRODUCT_NAME"/></translation>
<translation id="2079545284768500474">Cofnij</translation>
<translation id="6556866813142980365">Ponów</translation>
<translation id="7724603315864178912">Wytnij</translation>
<translation id="6896758677409633944">Kopiuj</translation>
<translation id="3943857333388298514">Wklej</translation>
<translation id="8261506727792406068">Usuń</translation>
<translation id="5037676449506322593">Wybierz wszystko</translation>
<translation id="5941711191222866238">Minimalizuj</translation>
<translation id="2498539833203011245">Zminimalizuj</translation>
<translation id="3265459715026181080">Zamknij okno</translation>
<translation id="6315493146179903667">Przenieś wszystko na wierzch</translation>
</translationbundle>


var _ = require('underscore');
var selftest = require('../tool-testing/selftest.js');
var files = require('../fs/files.js');
import { getUrl } from '../utils/http-helpers.js';
import { sleepMs } from '../utils/utils.js';

var Sandbox = selftest.Sandbox;

var MONGO_LISTENING =
  { stdout: " [initandlisten] waiting for connections on port" };

function startRun(sandbox) {
  var run = sandbox.run();
  run.match("myapp");
  run.matchBeforeExit("Started proxy");
  run.tellMongo(MONGO_LISTENING);
  run.matchBeforeExit("Started MongoDB");
  run.waitSecs(15);
  return run;
};

// Tests the actual cache logic used by coffeescript.
selftest.define("compiler plugin caching - coffee", () => {
  var s = new Sandbox({ fakeMongo: true });
  s.set("METEOR_WATCH_PRIORITIZE_CHANGED", "false");

  s.createApp("myapp", "caching-coffee");
  s.cd("myapp");
  // Ask them to print out when they build a file (instead of using it from the
  // cache) as well as when they load cache from disk.
  s.set('METEOR_COFFEESCRIPT_CACHE_DEBUG', 't');
  var run = startRun(s);

  // First program built (server or web.browser) compiles everything.
  run.match('CACHE(coffeescript): Ran (#1) on: ' + JSON.stringify(
    ['/f1.coffee', '/f2.coffee', '/f3.coffee', '/packages/local-pack/p.coffee']
  ));
  // Second program doesn't need to compile anything because compilation works
  // the same on both programs.
  run.match("CACHE(coffeescript): Ran (#2) on: []");
  // App prints this:
  run.match("Coffeescript X is 2 Y is 1 FromPackage is 4");

  s.write("f2.coffee", "share.Y = 'Y is 3'\n");
  // Only recompiles f2.
  run.match('CACHE(coffeescript): Ran (#3) on: ["/f2.coffee"]');
  // And other program doesn't even need to do f2.
  run.match("CACHE(coffeescript): Ran (#4) on: []");
  // Program prints this:
  run.match("Coffeescript X is 2 Y is 3 FromPackage is 4");

  // Force a rebuild of the local package without actually changing the
  // coffeescript file in it. This should not require us to coffee.compile
  // anything (for either program).
  s.append("packages/local-pack/package.js", "\n// foo\n");
  run.match("CACHE(coffeescript): Ran (#5) on: []");
  run.match("CACHE(coffeescript): Ran (#6) on: []");
  run.match("Coffeescript X is 2 Y is 3 FromPackage is 4");

  // But writing to the actual source file in the local package should
  // recompile.
  s.write("packages/local-pack/p.coffee", "FromPackage = 'FromPackage is 5'");
  run.match('CACHE(coffeescript): Ran (#7) on: ["/packages/local-pack/p.coffee"]');
  run.match('CACHE(coffeescript): Ran (#8) on: []');
  run.match("Coffeescript X is 2 Y is 3 FromPackage is 5");

  // We never should have loaded cache from disk, since we only made
  // each compiler once and there were no cache files at this point.
  run.forbid('CACHE(coffeescript): Loaded');

  // Kill the run. Change one coffee file and re-run.
  run.stop();
  s.write("f2.coffee", "share.Y = 'Y is edited'\n");
  run = startRun(s);

  // This time there's a cache to load!
  run.match('CACHE(coffeescript): Loaded /packages/local-pack/p.coffee');
  run.match('CACHE(coffeescript): Loaded /f1.coffee');
  run.match('CACHE(coffeescript): Loaded /f3.coffee');
  // And we only need to re-compiler the changed file, even though we restarted.
  run.match('CACHE(coffeescript): Ran (#1) on: ["/f2.coffee"]');
  run.match('CACHE(coffeescript): Ran (#2) on: []');

  run.match('Coffeescript X is 2 Y is edited FromPackage is 5');

  run.stop();
});

// Tests the actual cache logic used by less and stylus.
['less', 'stylus'].forEach((packageName) => {
  const extension = packageName === 'stylus' ? 'styl' : packageName;

  selftest.define("compiler plugin caching - " + packageName, () => {
    var s = new Sandbox({ fakeMongo: true });
    s.set("METEOR_WATCH_PRIORITIZE_CHANGED", "false");

    s.createApp("myapp", "caching-" + packageName);
    s.cd("myapp");
    // Ask them to print out when they build a file (instead of using it from
    // the cache) as well as when they load cache from disk.
    s.set(`METEOR_${ packageName.toUpperCase() }_CACHE_DEBUG`, "t");
    var run = startRun(s);

    const cacheMatch = selftest.markStack((message) => {
      run.match(`CACHE(${ packageName }): ${ message }`);
    });

    // First program built (web.browser) compiles everything.
    cacheMatch('Ran (#1) on: ' + JSON.stringify([
      // Though files in imports directories are compiled, they are marked
      // as lazy so they will not be loaded unless imported.
      "/imports/dotdot." + extension,
      "/subdir/nested-root." + extension,
      "/top." + extension
    ]));
    // There is no render execution in the server program, because it has
    // archMatching:'web'.  We'll see this more clearly when the next call later
    // is "#2" --- we didn't miss a call!
    // App prints this:
    run.waitSecs(15);
    run.match("Hello world");

    // Check that the CSS is what we expect.
    var checkCSS = selftest.markStack((borderStyleMap) => {
      var builtBrowserProgramDir = files.pathJoin(
        s.cwd, '.meteor', 'local', 'build', 'programs', 'web.browser');
      var cssFile = _.find(
        files.readdir(
          files.pathJoin(s.cwd, '.meteor/local/build/programs/web.browser')),
        path => path.match(/\.css$/)
      );
      selftest.expectTrue(cssFile);
      var actual = s.read(
        files.pathJoin('.meteor/local/build/programs/web.browser', cssFile));
      actual = actual.replace(/\s+/g, ' ');  // simplify whitespace
      var expected = _.map(borderStyleMap, (style, className) => {
        return '.' + className + " { border-style: " + style + "; }";
      }).join(' ');
      selftest.expectEqual(actual, expected);
    });
    var expectedBorderStyles = {
      el0: "dashed", el1: "dotted", el2: "solid", el3: "groove", el4: "ridge"};
    checkCSS(expectedBorderStyles);

    // Force a rebuild of the local package without actually changing the
    // preprocessor file in it. This should not require us to render anything.
    s.append("packages/local-pack/package.js", "\n// foo\n");
    cacheMatch('Ran (#2) on: []');
    run.waitSecs(15);
    run.match("Hello world");

    function setVariable(variableName, value) {
      switch (packageName) {
      case 'less':
        return `@${ variableName }: ${ value };\n`;
      case 'stylus':
        return `$${ variableName } = ${ value }\n`;
      }
    }
    function importLine(fileWithoutExtension) {
      switch (packageName) {
      case 'less':
        return `@import "${ fileWithoutExtension }.less";\n`;
      case 'stylus':
        return `@import "${ fileWithoutExtension }.styl"\n`;
      }
    }

    // Writing to a single file only re-renders the root that depends on it.
    s.write('packages/local-pack/p.' + extension,
            setVariable('el4-style', 'inset'));
    expectedBorderStyles.el4 = 'inset';
    cacheMatch(`Ran (#3) on: ["/top.${ extension }"]`);
    run.match("Client modified -- refreshing");
    checkCSS(expectedBorderStyles);

    // This works for changing a root too.
    s.write('subdir/nested-root.' + extension,
            '.el0 { border-style: double; }\n');
    expectedBorderStyles.el0 = 'double';
    cacheMatch(`Ran (#4) on: ["/subdir/nested-root.${ extension }"]`);
    run.match("Client modified -- refreshing");
    checkCSS(expectedBorderStyles);

    // Adding a new root works too.
    s.write('yet-another-root.' + extension,
            '.el6 { border-style: solid; }\n');
    expectedBorderStyles.el6 = 'solid';
    cacheMatch(`Ran (#5) on: ["/yet-another-root.${ extension }"]`);
    run.match("Client modified -- refreshing");
    checkCSS(expectedBorderStyles);

    // We never should have loaded cache from disk, since we only made
    // each compiler once and there were no cache files at this point.
    run.forbid('CACHE(${ packageName }): Loaded');

    // Kill the run. Change one file and re-run.
    run.stop();
    s.write('packages/local-pack/p.' + extension,
            setVariable('el4-style', 'double'));
    expectedBorderStyles.el4 = 'double';
    run = startRun(s);

    // This time there's a cache to load!  Note that for
    // MultiFileCachingCompiler we load all the cache entries, even for the
    // not-up-to-date file 'top', because we only key off of filename, not off
    // of cache key.
    cacheMatch('Loaded {}/subdir/nested-root.' + extension);
    cacheMatch('Loaded {}/top.' + extension);
    cacheMatch('Loaded {}/yet-another-root.' + extension);
    cacheMatch(`Ran (#1) on: ["/top.${ extension }"]`);
    run.waitSecs(15);
    run.match('Hello world');
    checkCSS(expectedBorderStyles);

    s.write('bad-import.' + extension, importLine('/foo/bad'));
    run.match('Errors prevented startup');
    switch (packageName) {
    case 'less':
      run.match('bad-import.less:1: Unknown import: /foo/bad.less');
      break;
    case 'stylus':
      run.match('bad-import.styl: Stylus compiler error: bad-import.styl:1');
      run.match('failed to locate @import file /foo/bad.styl');
      break;
    }
    run.match('Waiting for file change');

    run.stop();
  });
});

// Tests that rebuilding a compiler plugin re-instantiates the source processor,
// but other changes don't.
selftest.define("compiler plugin caching - local plugin", function () {
  var s = new Sandbox({ fakeMongo: true });

  s.createApp("myapp", "local-compiler-plugin");
  s.cd("myapp");

  var run = startRun(s);

  // The compiler gets used the first time...
  run.match("PrintmeCompiler invocation 1");
  // ... and the program runs the generated code.
  run.match("PMC: Print out bar");
  run.match("PMC: Print out foo");

  s.write("quux.printme", "And print out quux");
  // PrintmeCompiler gets reused.
  run.match("PrintmeCompiler invocation 2");
  // And the right output prints out
  run.match("PMC: Print out bar");
  run.match("PMC: Print out foo");
  run.match("PMC: And print out quux");

  // Restart meteor; see that the disk cache gets used.
  run.stop();
  run = startRun(s);
  // Disk cache gets us up to 3.
  run.match("PrintmeCompiler invocation 3");
  // And the right output prints out
  run.match("PMC: Print out bar");
  run.match("PMC: Print out foo");
  run.match("PMC: And print out quux");

  // Edit the compiler itself.
  s.write('packages/local-plugin/plugin.js',
          s.read('packages/local-plugin/plugin.js').replace(/PMC/, 'pmc'));
  // New PrintmeCompiler object, and empty disk cache dir.
  run.match("PrintmeCompiler invocation 1");
  // And the right output prints out (lower case now)
  run.match("pmc: Print out bar");
  run.match("pmc: Print out foo");
  run.match("pmc: And print out quux");

  run.stop();
});

// Test error on duplicate compiler plugins.
selftest.define("compiler plugins - duplicate extension", () => {
  const s = new Sandbox({ fakeMongo: true });
  s.set("METEOR_WATCH_PRIORITIZE_CHANGED", "false");

  s.createApp("myapp", "duplicate-compiler-extensions");
  s.cd("myapp");

  let run = startRun(s);
  run.match('Errors prevented startup');
  run.match('conflict: two packages');
  run.match('trying to handle *.myext');

  // Fix it by changing one extension.
  s.write('packages/local-plugin/plugin.js',
          s.read('packages/local-plugin/plugin.js').replace('myext', 'xext'));
  run.match('Modified -- restarting');

  run.stop();
});

// Test error when a source file no longer has an active plugin.
selftest.define("compiler plugins - inactive source", () => {
  const s = new Sandbox({ fakeMongo: true });

  // This app depends on the published package 'glasser:uses-sourcish', and
  // contains a local package 'local-plugin'.
  //
  // glasser:uses-sourcish depends on local-plugin and contains a file
  // 'foo.sourcish'. When glasser:uses-sourcish@0.0.1 was published, a local
  // copy of 'local-plugin' had a plugin which called registerCompiler for the
  // extension '*.sourcish', and so 'foo.sourcish' is in the published isopack
  // as a source file. However, the copy of 'local-plugin' currently in the test
  // app contains no plugins. So we hit this weird error.
  s.createApp('myapp', 'uses-published-package-with-inactive-source');
  s.cd('myapp');

  const run = s.run();
  run.match('myapp');
  run.matchBeforeExit('Started proxy');
  run.match('Errors prevented startup');
  run.match('no plugin found for foo.sourcish in glasser:use-sourcish');
  run.match('none is now');
  run.stop();
});

// Test error when the registerCompiler callback throws.
selftest.define("compiler plugins - compiler throws", () => {
  const s = new Sandbox({ fakeMongo: true });

  s.createApp('myapp', 'compiler-plugin-throws-on-instantiate');
  s.cd('myapp');

  const run = s.run('add', 'local-plugin');
  run.matchErr('Errors while adding packages');
  run.matchErr(
    'While running registerCompiler callback in package local-plugin');
  // XXX This is wrong! The path on disk is packages/local-plugin/plugin.js, but
  // at some point we switched to the servePath which is based on the *plugin*'s
  // "package" name.
  run.matchErr(
    /packages\/compilePrintme_plugin\.js:\d+:\d+: Error in my registerCompiler callback!/
  );
  run.expectExit(1);
});

// Test that compiler plugins can add static assets. Also tests `filenames`
// option to registerCompiler.
selftest.define("compiler plugins - compiler addAsset", () => {
  const s = new Sandbox({ fakeMongo: true });

  s.createApp('myapp', 'compiler-plugin-add-asset');
  s.cd('myapp');

  const run = startRun(s);
  // Test server-side asset.
  run.match("extension is null");  // test getExtension -> null
  run.match("Asset says Print out foo");

  // Test client-side asset.
  const body = getUrl('http://localhost:3000/foo.printme');
  selftest.expectEqual(body, 'Print out foo\n');

  run.stop();
});

// Test that a package can have a single file that is both source code and an
// asset
selftest.define("compiler plugins - addAssets", () => {
  const s = new Sandbox({ fakeMongo: true });

  s.createApp('myapp', 'compiler-plugin-asset-and-source');
  s.cd('myapp');

  const run = startRun(s);

  // Test server-side asset.
  run.match("Printing out my own source code!");

  // Test client-side asset.
  let body = getUrl('http://localhost:3000/packages/' +
    'asset-and-source/asset-and-source.js');
  selftest.expectTrue(body.indexOf('Printing out my own source code!') !== -1);

  // Test that deprecated API still works (added in 1.2.1 in response to people
  // having trouble upgrading to 1.2)
  s.write("packages/asset-and-source/package.js", `Package.describe({
      name: 'asset-and-source',
      version: '0.0.1'
    });

    Package.onUse(function(api) {
      api.addFiles('asset-and-source.js');
      api.addFiles('asset-and-source.js',
        ['client', 'server'], { isAsset: true });
    });
  `);

  // Test server-side asset.
  run.match("Printing out my own source code!");

  // Test client-side asset.
  body = getUrl('http://localhost:3000/packages/' +
    'asset-and-source/asset-and-source.js');
  selftest.expectTrue(body.indexOf('Printing out my own source code!') !== -1);

  // Test error messages for malformed package files
  s.write("packages/asset-and-source/package.js", `Package.describe({
      name: 'asset-and-source',
      version: '0.0.1'
    });

    Package.onUse(function(api) {
      api.addFiles('asset-and-source.js');
      api.addAssets('asset-and-source.js', ['client', 'server']);
      api.addFiles('asset-and-source.js');
    });
  `);

  run.match(/Duplicate source file/);

  s.write("packages/asset-and-source/package.js", `Package.describe({
      name: 'asset-and-source',
      version: '0.0.1'
    });

    Package.onUse(function(api) {
      api.addFiles('asset-and-source.js');
      api.addAssets('asset-and-source.js', ['client', 'server']);
      api.addAssets('asset-and-source.js', ['client', 'server']);
    });
  `);

  run.match(/Duplicate asset file/);

  s.write("packages/asset-and-source/package.js", `Package.describe({
      name: 'asset-and-source',
      version: '0.0.1'
    });

    Package.onUse(function(api) {
      api.addAssets('asset-and-source.js');
    });
  `);

  run.match(/requires a second argument/);

  run.stop();
});