text:
// 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 "chrome/browser/devtools/devtools_adb_bridge.h"

#include <map>
#include <vector>

#include "base/base64.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/json/json_reader.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread.h"
#include "base/values.h"
#include "chrome/browser/devtools/adb/android_rsa.h"
#include "chrome/browser/devtools/adb_client_socket.h"
#include "chrome/browser/devtools/adb_web_socket.h"
#include "chrome/browser/devtools/devtools_protocol.h"
#include "chrome/browser/devtools/devtools_target_impl.h"
#include "chrome/browser/devtools/devtools_window.h"
#include "chrome/browser/profiles/profile.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/devtools_client_host.h"
#include "content/public/browser/devtools_external_agent_proxy.h"
#include "content/public/browser/devtools_external_agent_proxy_delegate.h"
#include "content/public/browser/devtools_manager.h"
#include "content/public/browser/user_metrics.h"
#include "crypto/rsa_private_key.h"
#include "net/base/escape.h"
#include "net/base/net_errors.h"

using content::BrowserThread;

namespace {

const char kDeviceModelCommand[] = "shell:getprop ro.product.model";
const char kInstalledChromePackagesCommand[] = "shell:pm list packages";
const char kOpenedUnixSocketsCommand[] = "shell:cat /proc/net/unix";
const char kListProcessesCommand[] = "shell:ps";
const char kDumpsysCommand[] = "shell:dumpsys window policy";
const char kDumpsysScreenSizePrefix[] = "mStable=";

const char kUnknownModel[] = "Offline";

const char kPageListRequest[] = "GET /json HTTP/1.1\r\n\r\n";
const char kVersionRequest[] = "GET /json/version HTTP/1.1\r\n\r\n";
const char kClosePageRequest[] = "GET /json/close/%s HTTP/1.1\r\n\r\n";
const char kNewPageRequest[] = "GET /json/new HTTP/1.1\r\n\r\n";
const char kNewPageRequestWithURL[] = "GET /json/new?%s HTTP/1.1\r\n\r\n";
const char kActivatePageRequest[] =
    "GET /json/activate/%s HTTP/1.1\r\n\r\n";
const int kAdbPollingIntervalMs = 1000;

const char kUrlParam[] = "url";
const char kPageReloadCommand[] = "Page.reload";
const char kPageNavigateCommand[] = "Page.navigate";

const char kChromeDefaultName[] = "Chrome";
const char kChromeDefaultSocket[] = "chrome_devtools_remote";
const int kMinVersionNewWithURL = 32;
const int kNewPageNavigateDelayMs = 500;

const char kWebViewSocketPrefix[] = "webview_devtools_remote";
const char kWebViewNameTemplate[] = "WebView in %s";

typedef DevToolsAdbBridge::Callback Callback;
typedef std::vector<scoped_refptr<AndroidDevice> >
    AndroidDevices;
typedef base::Callback<void(const AndroidDevices&)> AndroidDevicesCallback;

struct BrowserDescriptor {
  const char* package;
  const char* socket;
  const char* display_name;
};

const BrowserDescriptor kBrowserDescriptors[] = {
  {
    "com.android.chrome",
    kChromeDefaultSocket,
    kChromeDefaultName
  },
  {
    "com.chrome.beta",
    kChromeDefaultSocket,
    "Chrome Beta"
  },
  {
    "com.google.android.apps.chrome_dev",
    kChromeDefaultSocket,
    "Chrome Dev"
  },
  {
    "com.google.android.apps.chrome",
    kChromeDefaultSocket,
    "Chromium"
  },
  {
    "org.chromium.content_shell_apk",
    "content_shell_devtools_remote",
    "Content Shell"
  },
  {
    "org.chromium.chrome.shell",
    "chrome_shell_devtools_remote",
    "Chrome Shell"
  },
  {
    "org.chromium.android_webview.shell",
    "webview_devtools_remote",
    "WebView Test Shell"
  }
};

const BrowserDescriptor* FindBrowserDescriptor(const std::string& package) {
  int count = sizeof(kBrowserDescriptors) / sizeof(kBrowserDescriptors[0]);
  for (int i = 0; i < count; i++)
    if (kBrowserDescriptors[i].package == package)
      return &kBrowserDescriptors[i];
  return NULL;
}

typedef std::map<std::string, const BrowserDescriptor*> DescriptorMap;

static DescriptorMap FindInstalledBrowserPackages(
    const std::string& response) {
  // Parse 'pm list packages' output which on Android looks like this:
  //
  // package:com.android.chrome
  // package:com.chrome.beta
  // package:com.example.app
  //
  DescriptorMap package_to_descriptor;
  const std::string package_prefix = "package:";
  std::vector<std::string> entries;
  Tokenize(response, "'\r\n", &entries);
  for (size_t i = 0; i < entries.size(); ++i) {
    if (entries[i].find(package_prefix) != 0)
      continue;
    std::string package = entries[i].substr(package_prefix.size());
    const BrowserDescriptor* descriptor = FindBrowserDescriptor(package);
    if (!descriptor)
      continue;
    package_to_descriptor[descriptor->package] = descriptor;
  }
  return package_to_descriptor;
}

typedef std::map<std::string, std::string> StringMap;

static void MapProcessesToPackages(const std::string& response,
                                   StringMap& pid_to_package,
                                   StringMap& package_to_pid) {
  // Parse 'ps' output which on Android looks like this:
  //
  // USER PID PPID VSIZE RSS WCHAN PC ? NAME
  //
  std::vector<std::string> entries;
  Tokenize(response, "\n", &entries);
  for (size_t i = 1; i < entries.size(); ++i) {
    std::vector<std::string> fields;
    Tokenize(entries[i], " \r", &fields);
    if (fields.size() < 9)
      continue;
    std::string pid = fields[1];
    std::string package = fields[8];
    pid_to_package[pid] = package;
    package_to_pid[package] = pid;
  }
}

typedef std::map<std::string,
                 scoped_refptr<DevToolsAdbBridge::RemoteBrowser> > BrowserMap;

static StringMap MapSocketsToProcesses(const std::string& response,
                                       const std::string& channel_pattern) {
  // Parse 'cat /proc/net/unix' output which on Android looks like this:
  //
  // Num       RefCount Protocol Flags    Type St Inode Path
  // 00000000: 00000002 00000000 00010000 0001 01 331813 /dev/socket/zygote
  // 00000000: 00000002 00000000 00010000 0001 01 358606 @xxx_devtools_remote
  // 00000000: 00000002 00000000 00010000 0001 01 347300 @yyy_devtools_remote
  //
  // We need to find records with paths starting from '@' (abstract socket)
  // and containing the channel pattern ("_devtools_remote").
  StringMap socket_to_pid;
  std::vector<std::string> entries;
  Tokenize(response, "\n", &entries);
  for (size_t i = 1; i < entries.size(); ++i) {
    std::vector<std::string> fields;
    Tokenize(entries[i], " \r", &fields);
    if (fields.size() < 8)
      continue;
    if (fields[3] != "00010000" || fields[5] != "01")
      continue;
    std::string path_field = fields[7];
    if (path_field.size() < 1 || path_field[0] != '@')
      continue;
    size_t socket_name_pos = path_field.find(channel_pattern);
    if (socket_name_pos == std::string::npos)
      continue;

    std::string socket = path_field.substr(1);

    std::string pid;
    size_t socket_name_end = socket_name_pos + channel_pattern.size();
    if (socket_name_end < path_field.size() &&
        path_field[socket_name_end] == '_') {
      pid = path_field.substr(socket_name_end + 1);
    }
    socket_to_pid[socket] = pid;
  }
  return socket_to_pid;
}

// AdbPagesCommand ------------------------------------------------------------

class AdbPagesCommand : public base::RefCountedThreadSafe<
    AdbPagesCommand,
    BrowserThread::DeleteOnUIThread> {
 public:
  typedef base::Callback<void(DevToolsAdbBridge::RemoteDevices*)> Callback;

  AdbPagesCommand(
      scoped_refptr<RefCountedAdbThread> adb_thread,
      const DevToolsAdbBridge::DeviceProviders& device_providers,
      const Callback& callback);

 private:
  friend struct BrowserThread::DeleteOnThread<
      BrowserThread::UI>;
  friend class base::DeleteHelper<AdbPagesCommand>;

  virtual ~AdbPagesCommand();
  void ProcessDeviceProviders();
  void ReceivedDevices(const AndroidDevices& devices);

  void ProcessSerials();
  void ReceivedModel(int result, const std::string& response);
  void ReceivedDumpsys(int result, const std::string& response);
  void ReceivedPackages(int result, const std::string& response);
  void ReceivedProcesses(
      const std::string& packages_response,
      int result,
      const std::string& processes_response);
  void ReceivedSockets(
      const std::string& packages_response,
      const std::string& processes_response,
      int result,
      const std::string& sockets_response);
  void ProcessSockets();
  void ReceivedVersion(int result, const std::string& response);
  void ReceivedPages(int result, const std::string& response);

  scoped_refptr<AndroidDevice> current_device() const {
    return devices_.back();
  }

  scoped_refptr<DevToolsAdbBridge::RemoteBrowser> current_browser() const {
    return browsers_.back();
  }

  void NextBrowser();
  void NextDevice();

  void Respond();

  void CreateBrowsers(const std::string& packages_response,
                      const std::string& processes_response,
                      const std::string& sockets_response);

  void ParseDumpsysResponse(const std::string& response);
  void ParseScreenSize(const std::string& str);

  scoped_refptr<RefCountedAdbThread> adb_thread_;
  Callback callback_;
  AndroidDevices devices_;
  DevToolsAdbBridge::RemoteBrowsers browsers_;
  scoped_ptr<DevToolsAdbBridge::RemoteDevices> remote_devices_;
  DevToolsAdbBridge::DeviceProviders device_providers_;
};

AdbPagesCommand::AdbPagesCommand(
    scoped_refptr<RefCountedAdbThread> adb_thread,
    const DevToolsAdbBridge::DeviceProviders& device_providers,
    const Callback& callback)
    : adb_thread_(adb_thread),
      callback_(callback),
      device_providers_(device_providers) {
  remote_devices_.reset(new DevToolsAdbBridge::RemoteDevices());

  ProcessDeviceProviders();
}

AdbPagesCommand::~AdbPagesCommand() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
}

void AdbPagesCommand::ProcessDeviceProviders() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (device_providers_.empty()) {
    adb_thread_->message_loop()->PostTask(
              FROM_HERE, base::Bind(&AdbPagesCommand::ProcessSerials, this));
    return;
  }

  const scoped_refptr<AndroidDeviceProvider>& device_provider =
      device_providers_.back();

  device_provider->QueryDevices(
      base::Bind(&AdbPagesCommand::ReceivedDevices, this));
}

void AdbPagesCommand::ReceivedDevices(const AndroidDevices& devices) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK(!device_providers_.empty());
  device_providers_.pop_back();

  devices_.insert(devices_.end(), devices.begin(), devices.end());

  if (!device_providers_.empty()) {
    ProcessDeviceProviders();
  } else {
    adb_thread_->message_loop()->PostTask(
          FROM_HERE, base::Bind(&AdbPagesCommand::ProcessSerials, this));
  }
}

void AdbPagesCommand::ProcessSerials() {
  DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
  if (devices_.size() == 0) {
    BrowserThread::PostTask(
        BrowserThread::UI, FROM_HERE,
        base::Bind(&AdbPagesCommand::Respond, this));
    return;
  }

  scoped_refptr<AndroidDevice> device = current_device();

  if (device->is_connected()) {
    device->RunCommand(kDeviceModelCommand,
                       base::Bind(&AdbPagesCommand::ReceivedModel, this));
  } else {
    device->set_model(kUnknownModel);
    remote_devices_->push_back(new DevToolsAdbBridge::RemoteDevice(device));
    NextDevice();
  }
}

void AdbPagesCommand::ReceivedModel(int result, const std::string& response) {
  DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
  if (result < 0) {
    NextDevice();
    return;
  }
  scoped_refptr<AndroidDevice> device = current_device();
  device->set_model(response);
  remote_devices_->push_back(new DevToolsAdbBridge::RemoteDevice(device));
  device->RunCommand(kDumpsysCommand,
                     base::Bind(&AdbPagesCommand::ReceivedDumpsys, this));
}

void AdbPagesCommand::ReceivedDumpsys(int result,
                                      const std::string& response) {
  DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
  if (result >= 0)
    ParseDumpsysResponse(response);

  current_device()->RunCommand(
      kInstalledChromePackagesCommand,
      base::Bind(&AdbPagesCommand::ReceivedPackages, this));
}

void AdbPagesCommand::ReceivedPackages(int result,
                                       const std::string& packages_response) {
  DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
  if (result < 0) {
    NextDevice();
    return;
  }
  current_device()->RunCommand(
      kListProcessesCommand,
      base::Bind(&AdbPagesCommand::ReceivedProcesses, this, packages_response));
}

void AdbPagesCommand::ReceivedProcesses(
    const std::string& packages_response,
    int result,
    const std::string& processes_response) {
  DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
  if (result < 0) {
    NextDevice();
    return;
  }
  current_device()->RunCommand(
      kOpenedUnixSocketsCommand,
      base::Bind(&AdbPagesCommand::ReceivedSockets,
                 this,
                 packages_response,
                 processes_response));
}

void AdbPagesCommand::ReceivedSockets(
    const std::string& packages_response,
    const std::string& processes_response,
    int result,
    const std::string& sockets_response) {
  DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
  if (result >= 0)
    CreateBrowsers(packages_response, processes_response, sockets_response);
  ProcessSockets();
}

void AdbPagesCommand::ProcessSockets() {
  DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
  if (browsers_.size() == 0) {
    NextDevice();
    return;
  }

  current_device()->HttpQuery(
      current_browser()->socket(),
      kVersionRequest,
      base::Bind(&AdbPagesCommand::ReceivedVersion, this));
}

void AdbPagesCommand::ReceivedVersion(int result,
                                      const std::string& response) {
  DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
  if (result < 0) {
    NextBrowser();
    return;
  }

  // Parse version, append to package name if available,
  scoped_ptr<base::Value> value(base::JSONReader::Read(response));
  base::DictionaryValue* dict;
  if (value && value->GetAsDictionary(&dict)) {
    std::string browser;
    if (dict->GetString("Browser", &browser)) {
      std::vector<std::string> parts;
      Tokenize(browser, "/", &parts);
      if (parts.size() == 2)
        current_browser()->set_version(parts[1]);
      else
        current_browser()->set_version(browser);
    }
    std::string package;
    if (dict->GetString("Android-Package", &package)) {
      const BrowserDescriptor* descriptor = FindBrowserDescriptor(package);
      if (descriptor)
        current_browser()->set_display_name(descriptor->display_name);
    }
  }

  current_device()->HttpQuery(
      current_browser()->socket(),
      kPageListRequest,
      base::Bind(&AdbPagesCommand::ReceivedPages, this));
}

void AdbPagesCommand::ReceivedPages(int result,
                                    const std::string& response) {
  DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
  if (result >= 0) {
    scoped_ptr<base::Value> value(base::JSONReader::Read(response));
    base::ListValue* list_value;
    if (value && value->GetAsList(&list_value))
      current_browser()->SetPageDescriptors(*list_value);
  }
  NextBrowser();
}

void AdbPagesCommand::NextBrowser() {
  browsers_.pop_back();
  ProcessSockets();
}

void AdbPagesCommand::NextDevice() {
  devices_.pop_back();
  ProcessSerials();
}

void AdbPagesCommand::Respond() {
  callback_.Run(remote_devices_.release());
}

void AdbPagesCommand::CreateBrowsers(
    const std::string& packages_response,
    const std::string& processes_response,
    const std::string& sockets_response) {
  DescriptorMap package_to_descriptor =
      FindInstalledBrowserPackages(packages_response);

  StringMap pid_to_package;
  StringMap package_to_pid;
  MapProcessesToPackages(processes_response, pid_to_package, package_to_pid);

  const std::string channel_pattern =
      base::StringPrintf(kDevToolsChannelNameFormat, "");

  StringMap socket_to_pid = MapSocketsToProcesses(sockets_response,
                                                  channel_pattern);

  scoped_refptr<DevToolsAdbBridge::RemoteDevice> remote_device =
      remote_devices_->back();

  // Create RemoteBrowser instances.
  BrowserMap package_to_running_browser;
  BrowserMap socket_to_unnamed_browser;
  for (StringMap::iterator it = socket_to_pid.begin();
      it != socket_to_pid.end(); ++it) {
    std::string socket = it->first;
    std::string pid = it->second;

    scoped_refptr<DevToolsAdbBridge::RemoteBrowser> browser =
        new DevToolsAdbBridge::RemoteBrowser(
            adb_thread_, remote_device->device(), socket);

    StringMap::iterator pit = pid_to_package.find(pid);
    if (pit != pid_to_package.end()) {
      std::string package = pit->second;
      package_to_running_browser[package] = browser;
      const BrowserDescriptor* descriptor = FindBrowserDescriptor(package);
      if (descriptor) {
        browser->set_display_name(descriptor->display_name);
      } else if (socket.find(kWebViewSocketPrefix) == 0) {
        browser->set_display_name(
            base::StringPrintf(kWebViewNameTemplate, package.c_str()));
      } else {
        browser->set_display_name(package);
      }
    } else {
      // Set fallback display name.
      std::string name = socket.substr(0, socket.find(channel_pattern));
      name[0] = base::ToUpperASCII(name[0]);
      browser->set_display_name(name);

      socket_to_unnamed_browser[socket] = browser;
    }
    remote_device->AddBrowser(browser);
  }

  browsers_ = remote_device->browsers();

  // Find installed packages not mapped to browsers.
  typedef std::multimap<std::string, const BrowserDescriptor*>
      DescriptorMultimap;
  DescriptorMultimap socket_to_descriptor;
  for (DescriptorMap::iterator it = package_to_descriptor.begin();
      it != package_to_descriptor.end(); ++it) {
    std::string package = it->first;
    const BrowserDescriptor* descriptor = it->second;

    if (package_to_running_browser.find(package) !=
        package_to_running_browser.end())
      continue;  // This package is already mapped to a browser.

    if (package_to_pid.find(package) != package_to_pid.end()) {
      // This package is running but not mapped to a browser.
      socket_to_descriptor.insert(
          DescriptorMultimap::value_type(descriptor->socket, descriptor));
      continue;
    }
  }

  // Try naming remaining unnamed browsers.
  for (DescriptorMultimap::iterator it = socket_to_descriptor.begin();
      it != socket_to_descriptor.end(); ++it) {
    std::string socket = it->first;
    const BrowserDescriptor* descriptor = it->second;

    if (socket_to_descriptor.count(socket) != 1)
      continue;  // No definitive match.

    BrowserMap::iterator bit = socket_to_unnamed_browser.find(socket);
    if (bit != socket_to_unnamed_browser.end())
      bit->second->set_display_name(descriptor->display_name);
  }
}

void AdbPagesCommand::ParseDumpsysResponse(const std::string& response) {
  std::vector<std::string> lines;
  Tokenize(response, "\r", &lines);
  for (size_t i = 0; i < lines.size(); ++i) {
    std::string line = lines[i];
    size_t pos = line.find(kDumpsysScreenSizePrefix);
    if (pos != std::string::npos) {
      ParseScreenSize(
          line.substr(pos + std::string(kDumpsysScreenSizePrefix).size()));
      break;
    }
  }
}

void AdbPagesCommand::ParseScreenSize(const std::string& str) {
  std::vector<std::string> pairs;
  Tokenize(str, "-", &pairs);
  if (pairs.size() != 2)
    return;

  int width;
  int height;
  std::vector<std::string> numbers;
  Tokenize(pairs[1].substr(1, pairs[1].size() - 2), ",", &numbers);
  if (numbers.size() != 2 ||
      !base::StringToInt(numbers[0], &width) ||
      !base::StringToInt(numbers[1], &height))
    return;

  remote_devices_->back()->set_screen_size(gfx::Size(width, height));
}

// AdbProtocolCommand ---------------------------------------------------------

class AdbProtocolCommand : public AdbWebSocket::Delegate {
 public:
  AdbProtocolCommand(
      scoped_refptr<RefCountedAdbThread> adb_thread,
      scoped_refptr<AndroidDevice> device,
      const std::string& socket_name,
      const std::string& debug_url,
      const std::string& command);

 private:
  virtual void OnSocketOpened() OVERRIDE;
  virtual void OnFrameRead(const std::string& message) OVERRIDE;
  virtual void OnSocketClosed(bool closed_by_device) OVERRIDE;
  virtual bool ProcessIncomingMessage(const std::string& message) OVERRIDE;

  scoped_refptr<RefCountedAdbThread> adb_thread_;
  const std::string command_;
  scoped_refptr<AdbWebSocket> web_socket_;

  DISALLOW_COPY_AND_ASSIGN(AdbProtocolCommand);
};

AdbProtocolCommand::AdbProtocolCommand(
    scoped_refptr<RefCountedAdbThread> adb_thread,
    scoped_refptr<AndroidDevice> device,
    const std::string& socket_name,
    const std::string& debug_url,
    const std::string& command)
    : adb_thread_(adb_thread),
      command_(command) {
  web_socket_ = new AdbWebSocket(
      device, socket_name, debug_url, adb_thread_->message_loop(), this);
}

void AdbProtocolCommand::OnSocketOpened() {
  web_socket_->SendFrame(command_);
  web_socket_->Disconnect();
}

void AdbProtocolCommand::OnFrameRead(const std::string& message) {}

void AdbProtocolCommand::OnSocketClosed(bool closed_by_device) {
  delete this;
}

bool AdbProtocolCommand::ProcessIncomingMessage(const std::string& message) {
  return false;
}

}  // namespace

const char kDevToolsChannelNameFormat[] = "%s_devtools_remote";

class AgentHostDelegate;

typedef std::map<std::string, AgentHostDelegate*> AgentHostDelegates;

base::LazyInstance<AgentHostDelegates>::Leaky g_host_delegates =
    LAZY_INSTANCE_INITIALIZER;

DevToolsAdbBridge::Wrapper::Wrapper() {
  bridge_ = new DevToolsAdbBridge();
}

DevToolsAdbBridge::Wrapper::~Wrapper() {
}

DevToolsAdbBridge* DevToolsAdbBridge::Wrapper::Get() {
  return bridge_.get();
}

// static
DevToolsAdbBridge::Factory* DevToolsAdbBridge::Factory::GetInstance() {
  return Singleton<DevToolsAdbBridge::Factory>::get();
}

// static
DevToolsAdbBridge* DevToolsAdbBridge::Factory::GetForProfile(
    Profile* profile) {
  DevToolsAdbBridge::Wrapper* wrapper =
      static_cast<DevToolsAdbBridge::Wrapper*>(GetInstance()->
          GetServiceForBrowserContext(profile, true));
  return wrapper ? wrapper->Get() : NULL;
}

DevToolsAdbBridge::Factory::Factory()
    : BrowserContextKeyedServiceFactory(
          "DevToolsAdbBridge",
          BrowserContextDependencyManager::GetInstance()) {}

DevToolsAdbBridge::Factory::~Factory() {}

KeyedService* DevToolsAdbBridge::Factory::BuildServiceInstanceFor(
    content::BrowserContext* context) const {
  return new DevToolsAdbBridge::Wrapper();
}

// AgentHostDelegate ----------------------------------------------------------

class AgentHostDelegate : public content::DevToolsExternalAgentProxyDelegate,
                          public AdbWebSocket::Delegate {
 public:
  static void Create(const std::string& id,
                     scoped_refptr<DevToolsAdbBridge::RemoteBrowser> browser,
                     const std::string& debug_url,
                     const std::string& frontend_url,
                     Profile* profile) {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    AgentHostDelegates::iterator it =
        g_host_delegates.Get().find(id);
    if (it != g_host_delegates.Get().end()) {
      it->second->OpenFrontend();
    } else if (!frontend_url.empty()) {
      new AgentHostDelegate(
          id, browser->device(), browser->socket(), debug_url,
          frontend_url, browser->adb_thread()->message_loop(), profile);
    }
  }

 private:
  AgentHostDelegate(
      const std::string& id,
      scoped_refptr<AndroidDevice> device,
      const std::string& socket_name,
      const std::string& debug_url,
      const std::string& frontend_url,
      base::MessageLoop* adb_message_loop,
      Profile* profile)
      : id_(id),
        frontend_url_(frontend_url),
        profile_(profile) {
    web_socket_ = new AdbWebSocket(
        device, socket_name, debug_url, adb_message_loop, this);
    g_host_delegates.Get()[id] = this;

    if (socket_name.find(kWebViewSocketPrefix) == 0) {
      content::RecordAction(
          base::UserMetricsAction("DevTools_InspectAndroidWebView"));
    } else {
      content::RecordAction(
          base::UserMetricsAction("DevTools_InspectAndroidPage"));
    }
  }

  void OpenFrontend() {
    if (!proxy_)
      return;
    DevToolsWindow::OpenExternalFrontend(
        profile_, frontend_url_, proxy_->GetAgentHost().get());
  }

  virtual ~AgentHostDelegate() {
    g_host_delegates.Get().erase(id_);
  }

  virtual void Attach() OVERRIDE {}

  virtual void Detach() OVERRIDE {
    web_socket_->Disconnect();
  }

  virtual void SendMessageToBackend(const std::string& message) OVERRIDE {
    web_socket_->SendFrame(message);
  }

  virtual void OnSocketOpened() OVERRIDE {
    proxy_.reset(content::DevToolsExternalAgentProxy::Create(this));
    OpenFrontend();
  }

  virtual void OnFrameRead(const std::string& message) OVERRIDE {
    proxy_->DispatchOnClientHost(message);
  }

  virtual void OnSocketClosed(bool closed_by_device) OVERRIDE {
    if (proxy_ && closed_by_device)
      proxy_->ConnectionClosed();
    delete this;
  }

  virtual bool ProcessIncomingMessage(const std::string& message) OVERRIDE {
    return false;
  }

  const std::string id_;
  const std::string frontend_url_;
  Profile* profile_;

  scoped_ptr<content::DevToolsExternalAgentProxy> proxy_;
  scoped_refptr<AdbWebSocket> web_socket_;
  DISALLOW_COPY_AND_ASSIGN(AgentHostDelegate);
};

//// RemotePageTarget ----------------------------------------------

class RemotePageTarget : public DevToolsTargetImpl {
 public:
  RemotePageTarget(scoped_refptr<DevToolsAdbBridge::RemoteBrowser> browser,
                   const base::DictionaryValue& value);
  virtual ~RemotePageTarget();

  // content::DevToolsTarget overrides:
  virtual bool IsAttached() const OVERRIDE;
  virtual bool Activate() const OVERRIDE;
  virtual bool Close() const OVERRIDE;

  // DevToolsTargetImpl overrides:
  virtual void Inspect(Profile* profile) const OVERRIDE;
  virtual void Reload() const OVERRIDE;

  void Navigate(const std::string& url) const;

 private:
  scoped_refptr<DevToolsAdbBridge::RemoteBrowser> browser_;
  std::string debug_url_;
  std::string frontend_url_;
  std::string remote_id_;
  DISALLOW_COPY_AND_ASSIGN(RemotePageTarget);
};

RemotePageTarget::RemotePageTarget(
    scoped_refptr<DevToolsAdbBridge::RemoteBrowser> browser,
    const base::DictionaryValue& value)
    : browser_(browser) {
  type_ = "adb_page";
  value.GetString("id", &remote_id_);
  std::string url;
  value.GetString("url", &url);
  url_ = GURL(url);
  value.GetString("title", &title_);
  title_ = base::UTF16ToUTF8(net::UnescapeForHTML(base::UTF8ToUTF16(title_)));
  value.GetString("description", &description_);
  std::string favicon_url;
  value.GetString("faviconUrl", &favicon_url);
  favicon_url_ = GURL(favicon_url);
  value.GetString("webSocketDebuggerUrl", &debug_url_);
  value.GetString("devtoolsFrontendUrl", &frontend_url_);

  if (remote_id_.empty() && !debug_url_.empty())  {
    // Target id is not available until Chrome 26. Use page id at the end of
    // debug_url_ instead. For attached targets the id will remain empty.
    std::vector<std::string> parts;
    Tokenize(debug_url_, "/", &parts);
    remote_id_ = parts[parts.size()-1];
  }

  if (debug_url_.find("ws://") == 0)
    debug_url_ = debug_url_.substr(5);
  else
    debug_url_ = "";

  size_t ws_param = frontend_url_.find("?ws");
  if (ws_param != std::string::npos)
    frontend_url_ = frontend_url_.substr(0, ws_param);
  if (frontend_url_.find("http:") == 0)
    frontend_url_ = "https:" + frontend_url_.substr(5);

  id_ = base::StringPrintf("%s:%s:%s",
      browser_->device()->serial().c_str(),
      browser_->socket().c_str(),
      remote_id_.c_str());
}

RemotePageTarget::~RemotePageTarget() {
}

bool RemotePageTarget::IsAttached() const {
  return debug_url_.empty();
}

void RemotePageTarget::Inspect(Profile* profile) const {
  std::string request = base::StringPrintf(kActivatePageRequest,
                                           remote_id_.c_str());
  base::Closure inspect_callback = base::Bind(&AgentHostDelegate::Create,
      id_, browser_, debug_url_, frontend_url_, profile);
  browser_->SendJsonRequest(request, inspect_callback);
}

bool RemotePageTarget::Activate() const {
  std::string request = base::StringPrintf(kActivatePageRequest,
                                           remote_id_.c_str());
  browser_->SendJsonRequest(request, base::Closure());
  return true;
}

bool RemotePageTarget::Close() const {
  if (IsAttached())
    return false;
  std::string request = base::StringPrintf(kClosePageRequest,
                                           remote_id_.c_str());
  browser_->SendJsonRequest(request, base::Closure());
  return true;
}

void RemotePageTarget::Reload() const {
  browser_->SendProtocolCommand(debug_url_, kPageReloadCommand, NULL);
}

void RemotePageTarget::Navigate(const std::string& url) const {
  base::DictionaryValue params;
  params.SetString(kUrlParam, url);
  browser_->SendProtocolCommand(debug_url_, kPageNavigateCommand, &params);
}

// DevToolsAdbBridge::RemoteBrowser -------------------------------------------

DevToolsAdbBridge::RemoteBrowser::RemoteBrowser(
    scoped_refptr<RefCountedAdbThread> adb_thread,
    scoped_refptr<AndroidDevice> device,
    const std::string& socket)
    : adb_thread_(adb_thread),
      device_(device),
      socket_(socket),
      page_descriptors_(new base::ListValue()) {
}

bool DevToolsAdbBridge::RemoteBrowser::IsChrome() const {
  return socket_.find(kChromeDefaultSocket) == 0;
}

DevToolsAdbBridge::RemoteBrowser::ParsedVersion
DevToolsAdbBridge::RemoteBrowser::GetParsedVersion() const {
  ParsedVersion result;
  std::vector<std::string> parts;
  Tokenize(version_, ".", &parts);
  for (size_t i = 0; i != parts.size(); ++i) {
    int value = 0;
    base::StringToInt(parts[i], &value);
    result.push_back(value);
  }
  return result;
}

std::vector<DevToolsTargetImpl*>
DevToolsAdbBridge::RemoteBrowser::CreatePageTargets() {
  std::vector<DevToolsTargetImpl*> result;
  for (size_t i = 0; i < page_descriptors_->GetSize(); ++i) {
    base::Value* item;
    page_descriptors_->Get(i, &item);
    if (!item)
      continue;
    base::DictionaryValue* dict;
    if (!item->GetAsDictionary(&dict))
      continue;
    result.push_back(new RemotePageTarget(this, *dict));
  }
  return result;
}

void DevToolsAdbBridge::RemoteBrowser::SetPageDescriptors(
    const base::ListValue& list) {
  page_descriptors_.reset(list.DeepCopy());
}

static void RespondOnUIThread(base::Closure callback, int, const std::string&) {
  if (!callback.is_null())
    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback);
}

void DevToolsAdbBridge::RemoteBrowser::SendJsonRequest(
    const std::string& request, base::Closure callback) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  adb_thread_->message_loop()->PostTask(FROM_HERE,
      base::Bind(&AndroidDevice::HttpQuery, device_, socket_, request,
          base::Bind(&RespondOnUIThread, callback)));
}

void DevToolsAdbBridge::RemoteBrowser::SendProtocolCommand(
    const std::string& debug_url,
    const std::string& method,
    base::DictionaryValue* params) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (debug_url.empty())
    return;
  DevToolsProtocol::Command command(1, method, params);
  new AdbProtocolCommand(
      adb_thread_, device_, socket_, debug_url, command.Serialize());
}

static void NoOp(int, const std::string&) {}

void DevToolsAdbBridge::RemoteBrowser::Open(const std::string& input_url) {
  GURL gurl(input_url);
  if (!gurl.is_valid()) {
    gurl = GURL("http://" + input_url);
    if (!gurl.is_valid())
     return;
  }
  std::string url = gurl.spec();

  ParsedVersion parsed_version = GetParsedVersion();
  if (IsChrome() &&
      !parsed_version.empty() &&
      parsed_version[0] >= kMinVersionNewWithURL) {
    std::string query = net::EscapeQueryParamValue(url, false /* use_plus */);
    std::string request =
        base::StringPrintf(kNewPageRequestWithURL, query.c_str());
    adb_thread_->message_loop()->PostTask(FROM_HERE,
        base::Bind(&AndroidDevice::HttpQuery,
            device_, socket_, request, base::Bind(&NoOp)));
  } else {
    adb_thread_->message_loop()->PostTask(FROM_HERE,
        base::Bind(&AndroidDevice::HttpQuery,
            device_, socket_, kNewPageRequest,
            base::Bind(&RemoteBrowser::PageCreatedOnHandlerThread, this, url)));
  }
}

void DevToolsAdbBridge::RemoteBrowser::PageCreatedOnHandlerThread(
    const std::string& url, int result, const std::string& response) {
  if (result < 0)
    return;
  // Navigating too soon after the page creation breaks navigation history
  // (crbug.com/311014). This can be avoided by adding a moderate delay.
  BrowserThread::PostDelayedTask(
      BrowserThread::UI, FROM_HERE,
      base::Bind(&RemoteBrowser::PageCreatedOnUIThread, this, response, url),
      base::TimeDelta::FromMilliseconds(kNewPageNavigateDelayMs));
}

void DevToolsAdbBridge::RemoteBrowser::PageCreatedOnUIThread(
    const std::string& response, const std::string& url) {
  scoped_ptr<base::Value> value(base::JSONReader::Read(response));
  base::DictionaryValue* dict;
  if (value && value->GetAsDictionary(&dict)) {
    RemotePageTarget new_page(this, *dict);
    new_page.Navigate(url);
  }
}

DevToolsAdbBridge::RemoteBrowser::~RemoteBrowser() {
}

// DevToolsAdbBridge::RemoteDevice --------------------------------------------

DevToolsAdbBridge::RemoteDevice::RemoteDevice(
    scoped_refptr<AndroidDevice> device)
    : device_(device) {
}

std::string DevToolsAdbBridge::RemoteDevice::GetSerial() {
  return device_->serial();
}

std::string DevToolsAdbBridge::RemoteDevice::GetModel() {
  return device_->model();
}

bool DevToolsAdbBridge::RemoteDevice::IsConnected() {
  return device_->is_connected();
}

void DevToolsAdbBridge::RemoteDevice::AddBrowser(
    scoped_refptr<RemoteBrowser> browser) {
  browsers_.push_back(browser);
}

DevToolsAdbBridge::RemoteDevice::~RemoteDevice() {
}

// DevToolsAdbBridge ----------------------------------------------------------

DevToolsAdbBridge::DevToolsAdbBridge()
    : adb_thread_(RefCountedAdbThread::GetInstance()),
      has_message_loop_(adb_thread_->message_loop() != NULL) {
}

void DevToolsAdbBridge::AddListener(Listener* listener) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (listeners_.empty())
    RequestRemoteDevices();
  listeners_.push_back(listener);
}

void DevToolsAdbBridge::RemoveListener(Listener* listener) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  Listeners::iterator it =
      std::find(listeners_.begin(), listeners_.end(), listener);
  DCHECK(it != listeners_.end());
  listeners_.erase(it);
}

bool DevToolsAdbBridge::HasDevToolsWindow(const std::string& agent_id) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  return g_host_delegates.Get().find(agent_id) != g_host_delegates.Get().end();
}

DevToolsAdbBridge::~DevToolsAdbBridge() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK(listeners_.empty());
}

void DevToolsAdbBridge::RequestRemoteDevices() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (!has_message_loop_)
    return;

  new AdbPagesCommand(
      adb_thread_, device_providers_,
      base::Bind(&DevToolsAdbBridge::ReceivedRemoteDevices, this));
}

void DevToolsAdbBridge::ReceivedRemoteDevices(RemoteDevices* devices_ptr) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  scoped_ptr<RemoteDevices> devices(devices_ptr);

  Listeners copy(listeners_);
  for (Listeners::iterator it = copy.begin(); it != copy.end(); ++it)
    (*it)->RemoteDevicesChanged(devices.get());

  if (listeners_.empty())
    return;

  BrowserThread::PostDelayedTask(
      BrowserThread::UI,
      FROM_HERE,
      base::Bind(&DevToolsAdbBridge::RequestRemoteDevices, this),
      base::TimeDelta::FromMilliseconds(kAdbPollingIntervalMs));
}


text:
<?php

###
# Photosynthesis Public Portfolio Listing
#
# PURPOSE
# Lists the contents of a single public portfolio.
#
###

# INCLUDES
# Include any files or libraries that are necessary for this specific
# page to function.
include_once '../includes/functions.inc.php';
include_once '../includes/settings.inc.php';
include_once '../includes/photosynthesis.inc.php';

# DECLARATIVE FUNCTIONS
# Run those functions that are necessary prior to loading this specific
# page.
$database = new Database;
$database->connect_mysqli();

# PAGE METADATA
$page_title = 'Photosynthesis';
$site_section = 'photosynthesis';

# LOCALIZE VARIABLES
$hash = $_GET['hash'];

# ADDITIONAL HTML HEADERS
$html_head = '<link rel="alternate" type="application/rss+xml" title="RSS 0.92" href="/photosynthesis/rss/portfolio/' . $hash . '/" />';

# INITIALIZE SESSION
session_start();

# If the user is logged in, get his data.
if (@logged_in())
{
    $user = @get_user();
}

# Start displaying the main page.
$page_body = '';

# Get this portfolio's basic data.
$sql = 'SELECT dashboard_portfolios.id, dashboard_portfolios.hash, dashboard_portfolios.name,
		dashboard_portfolios.notes, users.name AS user_name, dashboard_user_data.organization,
		users.url
		FROM dashboard_portfolios
		LEFT JOIN users
			ON dashboard_portfolios.user_id = users.id
		LEFT JOIN dashboard_user_data
			ON users.id = dashboard_user_data.user_id
		WHERE dashboard_portfolios.public = "y" AND dashboard_portfolios.hash="' . $hash . '"';
$result = mysqli_query($GLOBALS['db'], $sql);

# If this portfolio doesn't exist or isn't visible.
if (mysqli_num_rows($result) == 0)
{
    die('Invalid ID.');
}

# If this portfolio does exist.
else
{
    $portfolio = mysqli_fetch_array($result);
    $portfolio = array_map('stripslashes', $portfolio);

    # Increment the view count.
    $sql = 'UPDATE dashboard_portfolios
			SET view_count = view_count + 1
			WHERE id = ' . $portfolio['id'];
    mysqli_query($GLOBALS['db'], $sql);

    # Make the user closer to anonymous.
    $tmp = explode(' ', $portfolio['user_name']);
    if (count($tmp) > 1)
    {
        $portfolio['user_name'] = $tmp[0] . ' ' . $tmp[1]{0} . '.';
    }
    else
    {
        $portfolio['user_name'] = $tmp[0];
    }

    # Set the page title to the user's name.
    if (!empty($portfolio['organization']))
    {
        $page_title .= ' » ' . $portfolio['organization'];
    }
    else
    {
        $page_title .= ' » ' . $portfolio['user_name'];
    }
    $page_title .= ''s Portfolio';

    # Establish a sidebar.
    $page_sidebar = '

About This Portfolio

This is a collection of bills being tracked by ';

    # Make the user's name the link, unless there's an organization, in which case it should
    # be on that.
    if (empty($portfolio['url']))
    {
        $page_sidebar .= $portfolio['user_name'] . ' ';
    }
    elseif (empty($portfolio['organization']))
    {
        $page_sidebar .= '' . $portfolio['user_name'] . '';
    }
    else
    {
        $page_sidebar .= $portfolio['user_name'] . ' ';
    }
    if (!empty($portfolio['url']) && !empty($portfolio['organization']))
    {
        $page_sidebar .= ' for ' . $portfolio['organization'] . '';
    }
    elseif (!empty($portfolio['organization']))
    {
        $page_sidebar .= ' for ' . $portfolio['organization'];
    }
    $page_sidebar .= ' using the Photosynthesis bill-tracking tool.

Create a Photosynthesis account today and
			keep track of the legislation that interests you!

			<fb:like layout="button_count" show_faces="false" width="100" action="recommend"></fb:like>

';

    # Display a tag cloud.
    $sql = 'SELECT COUNT(*) AS count, tags.tag
			FROM tags
			LEFT JOIN dashboard_bills
				ON tags.bill_id = dashboard_bills.bill_id
			LEFT JOIN bills
				ON dashboard_bills.bill_id=bills.id
			LEFT JOIN sessions
				ON bills.session_id = sessions.id
			WHERE dashboard_bills.portfolio_id = ' . $portfolio['id'] . '
			AND sessions.year=' . SESSION_YEAR . '
			GROUP BY tags.tag
			ORDER BY tags.tag ASC';
    $result = mysqli_query($GLOBALS['db'], $sql);
    if (mysqli_num_rows($result) > 0)
    {
        $page_sidebar .= '

These Bills Are About . . .

';
        $top_tag = 1;
        $top_tag_size = 3;
        while ($tag = mysqli_fetch_array($result))
        {
            $tags[] = array_map('stripslashes', $tag);
            if ($tag['count'] > $top_tag)
            {
                $top_tag = $tag['count'];
            }
        }
        if ($top_tag == 1)
        {
            $top_tag_size = 1;
        }
        for ($i=0; $i<count($tags); $i++)
        {
            $font_size = round(($tags[$i]['count'] / $top_tag * $top_tag_size), 2);
            if ($font_size >= '.75')
            {
                $page_sidebar .= '
					' . $tags[$i]['tag'] . '
				';
            }
        }
        $page_sidebar .= '

';
    }

    # List all of the bills in this portfolio.
    $sql = 'SELECT bills.id, bills.number, bills.session_id, bills.chamber,
			bills.catch_line, bills.summary, bills.status, bills.outcome, sessions.year,
			representatives.name_formatted AS patron, representatives.shortname AS patron_shortname,
				(SELECT COUNT(*)
				FROM comments
				WHERE status = "published" AND bill_id = bills.id) AS comments,
			dashboard_bills.notes
			FROM bills
			LEFT JOIN sessions
				ON bills.session_id = sessions.id
			LEFT JOIN representatives
				ON bills.chief_patron_id = representatives.id
			LEFT JOIN dashboard_bills
				ON dashboard_bills.bill_id = bills.id
			WHERE dashboard_bills.portfolio_id = ' . $portfolio['id'] . '
			AND sessions.year=' . SESSION_YEAR . '
			ORDER BY bills.chamber DESC,
			SUBSTRING(bills.number FROM 1 FOR 2) ASC,
			CAST(LPAD(SUBSTRING(bills.number FROM 3), 4, "0") AS unsigned) ASC';
    $result = mysqli_query($GLOBALS['db'], $sql);
    $bill_count = mysqli_num_rows($result);
    if ($bill_count == 0)
    {
        die('Empty portfolio.');
    }

    # We've found bills in this portfolio.
    else
    {
        $page_body = '
';
        if ($bill_count == 1)
        {
            $page_body .= '
Just one bill is being tracked.
';
        }
        else
        {
            $page_body .= '
' . $bill_count . ' bills are being tracked.
';
        }

        while ($bill = mysqli_fetch_array($result))
        {
            $bill = array_map('stripslashes', $bill);

            # Remove the bit of the bill summary that just duplicates the catch line.
            $bill['summary'] = str_replace($bill['catch_line'], '', $bill['summary']);

            # Optionally establish a modifying status that will affect the look of the whole
            # bill in the portfolio.
            if ($bill['outcome'] == 'dead')
            {
                $bill['status_class'] = ' dead';
            }
            else
            {
                $bill['status_class'] = '';
            }

            $page_body .= '

' . $bill['catch_line']
                . ' (' . mb_strtoupper($bill['number']) . ')

Patron: '
                    . $bill['patron'] . '

				Status: ' . $bill['status'] . '
';

            # If the portfolio creator has provided notes on this bill.
            if (!empty($bill['notes']))
            {
                $page_body .= '

					' . nl2p($bill['notes']) . '

';
            }
            $page_body .= '
';
            if ($bill['comments'] == 0)
            {
                $page_body .= 'Be the first to comment on this bill';
            }
            else
            {
                if ($bill['comments'] == 1)
                {
                    $page_body .= 'One person has commented on this bill';
                }
                else
                {
                    $page_body .= 'There are ' . $bill['comments'] . ' comments about this bill';
                }
            }
            $page_body .= ' »

';
        }

        $page_body .= '
';
    }
}

# OUTPUT THE PAGE
$page = new Page;
$page->page_title = $page_title;
$page->page_body = $page_body;
$page->page_sidebar = $page_sidebar;
$page->site_section = $site_section;
$page->html_head = $html_head;
$page->process();

redpajama_set_name: RedPajamaGithub


text:
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
//@line 38 "e:\builds\moz2_slave\rel-m-rel-xr-w32-bld\build\toolkit\mozapps\update\nsUpdateServiceStub.js"
*/
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/FileUtils.jsm");

const Ci = Components.interfaces;

const DIR_UPDATES         = "updates";
const FILE_UPDATE_STATUS  = "update.status";

const KEY_APPDIR          = "XCurProcD";

//@line 54 "e:\builds\moz2_slave\rel-m-rel-xr-w32-bld\build\toolkit\mozapps\update\nsUpdateServiceStub.js"

//@line 56 "e:\builds\moz2_slave\rel-m-rel-xr-w32-bld\build\toolkit\mozapps\update\nsUpdateServiceStub.js"
const KEY_UPDROOT         = "UpdRootD";
//@line 58 "e:\builds\moz2_slave\rel-m-rel-xr-w32-bld\build\toolkit\mozapps\update\nsUpdateServiceStub.js"

/**
//@line 66 "e:\builds\moz2_slave\rel-m-rel-xr-w32-bld\build\toolkit\mozapps\update\nsUpdateServiceStub.js"
 */
function getUpdateDirNoCreate(pathArray) {
//@line 69 "e:\builds\moz2_slave\rel-m-rel-xr-w32-bld\build\toolkit\mozapps\update\nsUpdateServiceStub.js"
  try {
    let dir = FileUtils.getDir(KEY_UPDROOT, pathArray, false);
    return dir;
  } catch (e) {
  }
//@line 75 "e:\builds\moz2_slave\rel-m-rel-xr-w32-bld\build\toolkit\mozapps\update\nsUpdateServiceStub.js"
  return FileUtils.getDir(KEY_APPDIR, pathArray, false);
}

function UpdateServiceStub() {
  let statusFile = getUpdateDirNoCreate([DIR_UPDATES, "0"]);
  statusFile.append(FILE_UPDATE_STATUS);
  // If the update.status file exists then initiate post update processing.
  if (statusFile.exists()) {
    let aus = Components.classes["@mozilla.org/updates/update-service;1"].
              getService(Ci.nsIApplicationUpdateService).
              QueryInterface(Ci.nsIObserver);
    aus.observe(null, "post-update-processing", "");
  }
}
UpdateServiceStub.prototype = {
  observe: function(){},
  classID: Components.ID("{e43b0010-04ba-4da6-b523-1f92580bc150}"),
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver])
};

var NSGetFactory = XPCOMUtils.generateNSGetFactory([UpdateServiceStub]);


text:
.. _userguide:

User Guide
##########

The User Guide is being continuously updated, but please also consult the numerous
`examples <https://github.com/bokeh/bokeh/tree/master/examples>`_
to see the kinds of parameters that can be passed in to plotting functions in ``bokeh.plotting``, and look
at the `glyph examples <https://github.com/bokeh/bokeh/tree/master/examples/glyphs>`_ to see
the kinds of low-level object attributes that can be set to really customize a plot.

.. toctree::
   :maxdepth: 2

   user_guide/objects.rst
   user_guide/plotting.rst
   user_guide/charts.rst
   user_guide/server.rst
   user_guide/embedding.rst
   user_guide/widgets.rst
   user_guide/ar.rst
   user_guide/examples.rst
   user_guide/issues.rst


text:
namespace ooge
{
	class Cull;

	struct SceneDescription
	{
		SceneDescription()
		{
			ScreenWidth = 800;
			ScreenHeight = 600;
			EnableFullScreen = false;
			EnableAudio = false;
			SceneCuller = NULL;
			WindowName = std::string("OOGE Engine");
		}

		~SceneDescription() {}

		Int32 ScreenHeight;
		Int32 ScreenWidth;
		bool EnableAudio;
		bool EnableFullScreen;
		Cull *SceneCuller;
		std::string WindowName;

	};
}

#endif //_SCENE_DESCRIPTION_HPP


text:
/* pr36133.c

   This test ensures that conditional branches can use the condition codes
   written by shift instructions, without the need for an extra TST.  */

/* { dg-do compile }  */
/* { dg-options "-O2" }  */
/* { dg-final { scan-assembler-not "tst" } } */

void
f (unsigned int a)
{
  if (a >> 4)
    asm volatile ("nop");
  asm volatile ("nop");
}


text:
package azkaban.executor;

import azkaban.db.EncodingType;
import azkaban.db.DatabaseOperator;
import azkaban.db.SQLTransaction;
import azkaban.utils.GZIPUtils;
import azkaban.utils.JSONUtils;
import azkaban.utils.Pair;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.log4j.Logger;

@Singleton
public class ExecutionFlowDao {

  private static final Logger logger = Logger.getLogger(ExecutionFlowDao.class);
  private final DatabaseOperator dbOperator;

  @Inject
  public ExecutionFlowDao(final DatabaseOperator dbOperator) {
    this.dbOperator = dbOperator;
  }

  public synchronized void uploadExecutableFlow(final ExecutableFlow flow)
      throws ExecutorManagerException {
    final String INSERT_EXECUTABLE_FLOW = "INSERT INTO execution_flows "
        + "(project_id, flow_id, version, status, submit_time, submit_user, update_time) "
        + "values (?,?,?,?,?,?,?)";
    final long submitTime = System.currentTimeMillis();
    flow.setStatus(Status.PREPARING);

    /**
     * Why we need a transaction to get last insert ID?
     * Because "SELECT LAST_INSERT_ID()" needs to have the same connection
     * as inserting the new entry.
     * See https://dev.mysql.com/doc/refman/5.7/en/information-functions.html#function_last-insert-id
     */
    final SQLTransaction<Long> insertAndGetLastID = transOperator -> {
      transOperator.update(INSERT_EXECUTABLE_FLOW, flow.getProjectId(),
          flow.getFlowId(), flow.getVersion(), Status.PREPARING.getNumVal(),
          submitTime, flow.getSubmitUser(), submitTime);
      transOperator.getConnection().commit();
      return transOperator.getLastInsertId();
    };

    try {
      final long id = this.dbOperator.transaction(insertAndGetLastID);
      logger.info("Flow given " + flow.getFlowId() + " given id " + id);
      flow.setExecutionId((int) id);
      updateExecutableFlow(flow);
    } catch (final SQLException e) {
      throw new ExecutorManagerException("Error creating execution.", e);
    }
  }

  List<ExecutableFlow> fetchFlowHistory(final int skip, final int num)
      throws ExecutorManagerException {
    try {
      return this.dbOperator.query(FetchExecutableFlows.FETCH_ALL_EXECUTABLE_FLOW_HISTORY,
          new FetchExecutableFlows(), skip, num);
    } catch (final SQLException e) {
      throw new ExecutorManagerException("Error fetching flow History", e);
    }
  }

  List<ExecutableFlow> fetchFlowHistory(final int projectId, final String flowId,
      final int skip, final int num)
      throws ExecutorManagerException {
    try {
      return this.dbOperator.query(FetchExecutableFlows.FETCH_EXECUTABLE_FLOW_HISTORY,
          new FetchExecutableFlows(), projectId, flowId, skip, num);
    } catch (final SQLException e) {
      throw new ExecutorManagerException("Error fetching flow history", e);
    }
  }

  public List<Pair<ExecutionReference, ExecutableFlow>> fetchQueuedFlows()
      throws ExecutorManagerException {
    try {
      return this.dbOperator.query(FetchQueuedExecutableFlows.FETCH_QUEUED_EXECUTABLE_FLOW,
          new FetchQueuedExecutableFlows());
    } catch (final SQLException e) {
      throw new ExecutorManagerException("Error fetching active flows", e);
    }
  }

  List<ExecutableFlow> fetchFlowHistory(final int projectId, final String flowId,
      final int skip, final int num,
      final Status status)
      throws ExecutorManagerException {
    try {
      return this.dbOperator.query(FetchExecutableFlows.FETCH_EXECUTABLE_FLOW_BY_STATUS,
          new FetchExecutableFlows(), projectId, flowId, status.getNumVal(), skip, num);
    } catch (final SQLException e) {
      throw new ExecutorManagerException("Error fetching active flows", e);
    }
  }

  List<ExecutableFlow> fetchRecentlyFinishedFlows(final Duration maxAge)
      throws ExecutorManagerException {
    try {
      return this.dbOperator.query(FetchRecentlyFinishedFlows.FETCH_RECENTLY_FINISHED_FLOW,
          new FetchRecentlyFinishedFlows(), System.currentTimeMillis() - maxAge.toMillis(),
          Status.SUCCEEDED.getNumVal(), Status.KILLED.getNumVal(),
          Status.FAILED.getNumVal());
    } catch (final SQLException e) {
      throw new ExecutorManagerException("Error fetching recently finished flows", e);
    }
  }

  List<ExecutableFlow> fetchFlowHistory(final String projContain, final String flowContains,
      final String userNameContains, final int status,
      final long startTime, final long endTime,
      final int skip, final int num)
      throws ExecutorManagerException {
    String query = FetchExecutableFlows.FETCH_BASE_EXECUTABLE_FLOW_QUERY;
    final List<Object> params = new ArrayList<>();

    boolean first = true;
    if (projContain != null && !projContain.isEmpty()) {
      query += " JOIN projects p ON ef.project_id = p.id WHERE name LIKE ?";
      params.add('%' + projContain + '%');
      first = false;
    }

    // todo kunkun-tang: we don't need the below complicated logics. We should just use a simple way.
    if (flowContains != null && !flowContains.isEmpty()) {
      if (first) {
        query += " WHERE ";
        first = false;
      } else {
        query += " AND ";
      }

      query += " flow_id LIKE ?";
      params.add('%' + flowContains + '%');
    }

    if (userNameContains != null && !userNameContains.isEmpty()) {
      if (first) {
        query += " WHERE ";
        first = false;
      } else {
        query += " AND ";
      }
      query += " submit_user LIKE ?";
      params.add('%' + userNameContains + '%');
    }

    if (status != 0) {
      if (first) {
        query += " WHERE ";
        first = false;
      } else {
        query += " AND ";
      }
      query += " status = ?";
      params.add(status);
    }

    if (startTime > 0) {
      if (first) {
        query += " WHERE ";
        first = false;
      } else {
        query += " AND ";
      }
      query += " start_time > ?";
      params.add(startTime);
    }

    if (endTime > 0) {
      if (first) {
        query += " WHERE ";
      } else {
        query += " AND ";
      }
      query += " end_time < ?";
      params.add(endTime);
    }

    if (skip > -1 && num > 0) {
      query += "  ORDER BY exec_id DESC LIMIT ?, ?";
      params.add(skip);
      params.add(num);
    }

    try {
      return this.dbOperator.query(query, new FetchExecutableFlows(), params.toArray());
    } catch (final SQLException e) {
      throw new ExecutorManagerException("Error fetching active flows", e);
    }
  }

  void updateExecutableFlow(final ExecutableFlow flow) throws ExecutorManagerException {
    updateExecutableFlow(flow, EncodingType.GZIP);
  }

  private void updateExecutableFlow(final ExecutableFlow flow, final EncodingType encType)
      throws ExecutorManagerException {
    final String UPDATE_EXECUTABLE_FLOW_DATA =
        "UPDATE execution_flows "
            + "SET status=?,update_time=?,start_time=?,end_time=?,enc_type=?,flow_data=? "
            + "WHERE exec_id=?";

    final String json = JSONUtils.toJSON(flow.toObject());
    byte[] data = null;
    try {
      final byte[] stringData = json.getBytes("UTF-8");
      data = stringData;
      // Todo kunkun-tang: use a common method to transform stringData to data.
      if (encType == EncodingType.GZIP) {
        data = GZIPUtils.gzipBytes(stringData);
      }
    } catch (final IOException e) {
      throw new ExecutorManagerException("Error encoding the execution flow.");
    }

    try {
      this.dbOperator.update(UPDATE_EXECUTABLE_FLOW_DATA, flow.getStatus()
          .getNumVal(), flow.getUpdateTime(), flow.getStartTime(), flow
          .getEndTime(), encType.getNumVal(), data, flow.getExecutionId());
    } catch (final SQLException e) {
      throw new ExecutorManagerException("Error updating flow.", e);
    }
  }

  public ExecutableFlow fetchExecutableFlow(final int execId) throws ExecutorManagerException {
    final FetchExecutableFlows flowHandler = new FetchExecutableFlows();
    try {
      final List<ExecutableFlow> properties = this.dbOperator
          .query(FetchExecutableFlows.FETCH_EXECUTABLE_FLOW, flowHandler, execId);
      if (properties.isEmpty()) {
        return null;
      } else {
        return properties.get(0);
      }
    } catch (final SQLException e) {
      throw new ExecutorManagerException("Error fetching flow id " + execId, e);
    }
  }

  public static class FetchExecutableFlows implements
      ResultSetHandler<List<ExecutableFlow>> {

    static String FETCH_BASE_EXECUTABLE_FLOW_QUERY =
        "SELECT ef.exec_id, ef.enc_type, ef.flow_data FROM execution_flows ef";
    static String FETCH_EXECUTABLE_FLOW =
        "SELECT exec_id, enc_type, flow_data FROM execution_flows "
            + "WHERE exec_id=?";
    static String FETCH_ALL_EXECUTABLE_FLOW_HISTORY =
        "SELECT exec_id, enc_type, flow_data FROM execution_flows "
            + "ORDER BY exec_id DESC LIMIT ?, ?";
    static String FETCH_EXECUTABLE_FLOW_HISTORY =
        "SELECT exec_id, enc_type, flow_data FROM execution_flows "
            + "WHERE project_id=? AND flow_id=? "
            + "ORDER BY exec_id DESC LIMIT ?, ?";
    static String FETCH_EXECUTABLE_FLOW_BY_STATUS =
        "SELECT exec_id, enc_type, flow_data FROM execution_flows "
            + "WHERE project_id=? AND flow_id=? AND status=? "
            + "ORDER BY exec_id DESC LIMIT ?, ?";

    @Override
    public List<ExecutableFlow> handle(final ResultSet rs) throws SQLException {
      if (!rs.next()) {
        return Collections.emptyList();
      }

      final List<ExecutableFlow> execFlows = new ArrayList<>();
      do {
        final int id = rs.getInt(1);
        final int encodingType = rs.getInt(2);
        final byte[] data = rs.getBytes(3);

        if (data != null) {
          final EncodingType encType = EncodingType.fromInteger(encodingType);
          try {
            final ExecutableFlow exFlow =
                ExecutableFlow.createExecutableFlowFromObject(
                    GZIPUtils.transformBytesToObject(data, encType));
            execFlows.add(exFlow);
          } catch (final IOException e) {
            throw new SQLException("Error retrieving flow data " + id, e);
          }
        }
      } while (rs.next());

      return execFlows;
    }
  }

  /**
   * JDBC ResultSetHandler to fetch queued executions
   */
  private static class FetchQueuedExecutableFlows implements
      ResultSetHandler<List<Pair<ExecutionReference, ExecutableFlow>>> {

    // Select queued unassigned flows
    private static final String FETCH_QUEUED_EXECUTABLE_FLOW =
        "SELECT exec_id, enc_type, flow_data FROM execution_flows"
            + " Where executor_id is NULL AND status = "
            + Status.PREPARING.getNumVal();

    @Override
    public List<Pair<ExecutionReference, ExecutableFlow>> handle(final ResultSet rs)
        throws SQLException {
      if (!rs.next()) {
        return Collections.emptyList();
      }

      final List<Pair<ExecutionReference, ExecutableFlow>> execFlows =
          new ArrayList<>();
      do {
        final int id = rs.getInt(1);
        final int encodingType = rs.getInt(2);
        final byte[] data = rs.getBytes(3);

        if (data == null) {
          logger.error("Found a flow with empty data blob exec_id: " + id);
        } else {
          final EncodingType encType = EncodingType.fromInteger(encodingType);
          try {
            final ExecutableFlow exFlow =
                ExecutableFlow.createExecutableFlowFromObject(
                    GZIPUtils.transformBytesToObject(data, encType));
            final ExecutionReference ref = new ExecutionReference(id);
            execFlows.add(new Pair<>(ref, exFlow));
          } catch (final IOException e) {
            throw new SQLException("Error retrieving flow data " + id, e);
          }
        }
      } while (rs.next());

      return execFlows;
    }
  }

  private static class FetchRecentlyFinishedFlows implements
      ResultSetHandler<List<ExecutableFlow>> {

    // Execution_flows table is already indexed by end_time
    private static final String FETCH_RECENTLY_FINISHED_FLOW =
        "SELECT exec_id, enc_type, flow_data FROM execution_flows "
            + "WHERE end_time > ? AND status IN (?, ?, ?)";

    @Override
    public List<ExecutableFlow> handle(
        final ResultSet rs) throws SQLException {
      if (!rs.next()) {
        return Collections.emptyList();
      }

      final List<ExecutableFlow> execFlows = new ArrayList<>();
      do {
        final int id = rs.getInt(1);
        final int encodingType = rs.getInt(2);
        final byte[] data = rs.getBytes(3);

        if (data != null) {
          final EncodingType encType = EncodingType.fromInteger(encodingType);
          try {
            final ExecutableFlow exFlow =
                ExecutableFlow.createExecutableFlowFromObject(
                    GZIPUtils.transformBytesToObject(data, encType));
            execFlows.add(exFlow);
          } catch (final IOException e) {
            throw new SQLException("Error retrieving flow data " + id, e);
          }
        }
      } while (rs.next());
      return execFlows;
    }
  }
}


text:
class Scene
{
public:
    Scene()
    {
        m_ActiveObject = 0;
    }

    ~Scene(){}

    void addObject(Object *_object);
    void setActiveObject(QString _name);
    void noActiveObject();

    int            getNumberOfObjects();
    Object*        getObject(const int &_i);
    Object*        getActiveObject();
    QList<Object*> getObjects();

private:
    QList<Object*>  m_Objects;
    Object         *m_ActiveObject;
};

#endif // SCENE_H


text:
import re

class SwitchConfiguration(object):
    def __init__(self, ip, name="", auto_enabled=False, privileged_passwords=None, ports=None, vlans=None, objects_overrides=None):
        self.ip = ip
        self.name = name
        self.privileged_passwords = privileged_passwords or []
        self.auto_enabled = auto_enabled
        self.vlans = []
        self.ports = []
        self.vrfs = [VRF('DEFAULT-LAN')]
        self.locked = False
        self.objects_factory = {
            "VRF": VRF,
            "Vlan": Vlan,
            "Port": Port,
            "VRRP": VRRP,
            "VlanPort": VlanPort,
            "AggregatedPort": AggregatedPort,
        }

        if vlans:
            [self.add_vlan(v) for v in vlans]
        if ports:
            [self.add_port(p) for p in ports]
        if objects_overrides:
            self.objects_factory.update(objects_overrides)

    def new(self, class_name, *args, **kwargs):
        return self.objects_factory[class_name](*args, **kwargs)

    def get_vlan(self, number):
        return next((vlan for vlan in self.vlans if vlan.number == number), None)

    def get_vlan_by_name(self, name):
        return next((vlan for vlan in self.vlans if vlan.name == name), None)

    def add_vlan(self, vlan):
        self.vlans.append(vlan)
        vlan.switch_configuration = self

    def remove_vlan(self, vlan):
        vlan.switch_configuration = None
        self.vlans.remove(vlan)

    def get_port(self, name):
        return next((port for port in self.ports if port.name == name), None)

    def add_port(self, port):
        self.ports.append(port)
        port.switch_configuration = self

    def remove_port(self, port):
        port.switch_configuration = None
        self.ports.remove(port)

    def get_port_by_partial_name(self, name):
        partial_name, number = split_port_name(name.lower())

        return next((port for port in self.ports if port.name.lower().startswith(partial_name.strip()) and port.name.lower().endswith(number.strip())), None)

    def get_port_and_ip_by_ip(self, ip_string):
        for port in filter(lambda e: isinstance(e, VlanPort), self.ports):
            for ip in port.ips:
                if ip_string in ip:
                    return port, ip
        return None, None

    def add_vrf(self, vrf):
        if not self.get_vrf(vrf.name):
            self.vrfs.append(vrf)

    def get_vrf(self, name):
        return next((vrf for vrf in self.vrfs if vrf.name == name), None)

    def remove_vrf(self, name):
        vrf = self.get_vrf(name)
        if vrf:
            self.vrfs.remove(vrf)
            for port in self.ports:
                if port.vrf and port.vrf.name == name:
                    port.vrf = None

class VRF(object):
    def __init__(self, name):
        self.name = name

class Vlan(object):
    def __init__(self, number=None, name=None, description=None, switch_configuration=None):
        self.number = number
        self.name = name
        self.description = description
        self.switch_configuration = switch_configuration

class Port(object):
    def __init__(self, name):
        self.name = name
        self.switch_configuration = None
        self.reset()

    def reset(self):
        self.description = None
        self.mode = None
        self.access_vlan = None
        self.trunk_vlans = None
        self.trunk_native_vlan = None
        self.trunk_encapsulation_mode = None
        self.shutdown = None
        self.vrf = None
        self.speed = None
        self.auto_negotiation = None
        self.aggregation_membership = None
        self.vendor_specific = {}
        self.ip_helpers = []
        self.lldp_transmit = None
        self.lldp_receive = None
        self.lldp_med_transmit_capabilities = None
        self.lldp_med_transmit_network_policy = None
        self.spanning_tree = None
        self.spanning_tree_portfast = None

    def get_subname(self, length):
        name, number = split_port_name(self.name)
        return name[:length] + number

class VRRP(object):
    def __init__(self, group_id):
        self.group_id = group_id
        self.ip_addresses = None
        self.description = None
        self.authentication = None
        self.timers_hello = None
        self.timers_hold = False
        self.priority = None
        self.track = {}
        self.preempt = True
        self.preempt_delay_minimum = None
        self.activated = None
        self.advertising = None

class VlanPort(Port):
    def __init__(self, vlan_id, *args, **kwargs):
        super(VlanPort, self).__init__(*args, **kwargs)

        self.vlan_id = vlan_id
        self.access_group_in = None
        self.access_group_out = None
        self.ips = []
        self.secondary_ips = []
        self.vrrp_common_authentication = None
        self.vrrps = []

    def get_vrrp_group(self, group):
        return next((vrrp for vrrp in self.vrrps if vrrp.group_id == group), None)

    def add_ip(self, ip_network):
        existing_ip = next((ip for ip in self.ips if ip.ip == ip_network.ip), None)
        if existing_ip:
            self.ips[self.ips.index(existing_ip)] = ip_network
        else:
            self.ips.append(ip_network)

    def add_secondary_ip(self, ip_network):
        self.secondary_ips.append(ip_network)

    def remove_ip(self, ip_network):
        for i, ip in enumerate(self.ips):
            if ip.ip == ip_network.ip:
                self.ips.pop(i)
                break

    def remove_secondary_ip(self, deleting_ip):
        ip = next((ip for ip in self.secondary_ips if ip.ip == deleting_ip.ip), None)
        if ip:
            self.secondary_ips.remove(ip)

class AggregatedPort(Port):
    def __init__(self, *args, **kwargs):
        super(AggregatedPort, self).__init__(*args, **kwargs)

        self.lacp_active = False
        self.lacp_periodic = None

    def get_child_ports_linked_to_a_machine(self):
        return [p for p in self.switch_configuration.ports if p.aggregation_membership == self.name and p.link_name is not None]

def split_port_name(name):
    number_start, number_len = re.compile('\d').search(name).span()
    return name[0:number_start], name[number_start:]


text:
//  代码地址: https://github.com/iphone5solo/PYPhotosView
//  代码地址: http://code4app.com/thread-8612-1-1.html
//  Created by CoderKo1o.
//  Copyright © 2016年 iphone5solo. All rights reserved.
//

#import "PYPhotosView.h"
#import "PYPhotoView.h"
#import "PYPhoto.h"
#import "PYPhotoBrowserConst.h"
#import "PYPhotosViewController.h"
#import "PYPhotosReaderController.h"
#import "PYPhotosPreviewController.h"
NS_ASSUME_NONNULL_BEGIN
@interface PYPhotosView()

/** 图片大小*/
@property (nonatomic, assign) CGSize photoSize;

/** 添加图片按钮*/
@property (nonatomic, strong) UIButton *addImageButton;

/** 记录scrollerView的x值 */
@property (nonatomic, assign) CGFloat originalX;

@end

static PYPhotosViewController *_handleController;
static NSInteger _photosViewCount;

@implementation PYPhotosView

@dynamic delegate;

#pragma mark - 初始化
- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {
        // 只用一个控制器
        if (!_handleController) _handleController = [[PYPhotosViewController alloc] init];
        _photosViewCount++;
    }
    return self;
}

- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        if (!_handleController) _handleController = [[PYPhotosViewController alloc] init];
        _photosViewCount++;
        [self setupBaseConfig];
    }
    return self;
}

- (instancetype)init
{
    if (self = [super init]) {
        [self setupBaseConfig];
    }
    return self;
}

- (void)setupBaseConfig {
    // 初始化
    self.photoMargin = PYPhotoMargin;
    self.photoWidth = PYPhotoWidth;
    self.photoHeight = PYPhotoHeight;
    self.photosMaxCol = PYPhotosMaxCol;
    self.imagesMaxCountWhenWillCompose = PYImagesMaxCountWhenWillCompose;
    self.showsVerticalScrollIndicator = NO;
    self.showsHorizontalScrollIndicator = NO;
    self.autoRotateImage = YES;
    self.pageType = PYPhotosViewPageTypeControll;
    self.originalX = 0;
    self.pagingEnabled = NO;
    self.autoLayoutWithWeChatSytle = YES;
    self.showDuration = 0.5;
    self.hiddenDuration = 0.5;
}

+ (instancetype)photosView
{
    return [[self alloc] init];
}

+ (instancetype)photosViewWithThumbnailUrls:(NSArray<NSString *> *)thumbnailUrls originalUrls:(NSArray<NSString *> *)originalUrls
{
    PYPhotosView *photosView = [self photosView];
    photosView.thumbnailUrls = thumbnailUrls;
    photosView.originalUrls = originalUrls;
    return photosView;
}

+ (instancetype)photosViewWithImages:(NSMutableArray<UIImage *> *)images
{
    PYPhotosView *photosView = [self photosView];
    photosView.images = images;
    return photosView;
}

+ (instancetype)photosViewWithThumbnailUrls:(NSArray<NSString *> *)thumbnailUrls originalUrls:(NSArray<NSString *> *)originalUrls layoutType:(PYPhotosViewLayoutType)type
{
    PYPhotosView *photosView = [self photosViewWithThumbnailUrls:thumbnailUrls originalUrls:originalUrls];
    photosView.layoutType = type;
    return photosView;
}

+ (instancetype)photosViewWithThumbnailUrls:(NSArray<NSString *> *)thumbnailUrls originalUrls:(NSArray<NSString *> *)originalUrls photosMaxCol:(NSInteger)maxCol
{
    PYPhotosView *photosView = [self photosViewWithThumbnailUrls:thumbnailUrls originalUrls:originalUrls];
    photosView.photosMaxCol = maxCol;
    return photosView;
}

- (void)dealloc
{
    _photosViewCount--;
    if (0 == _photosViewCount) { // 没有photosView了 可以销毁了
        _handleController = nil;
    }
}

/* 返回最佳处理事件的view */
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event{
    // 判断这个点是否在subView上
    for (UIView *subView in self.subviews) {
        if (CGRectContainsPoint(subView.frame, point)) { // 触摸点在subView上
            return [super hitTest:point withEvent:event];
        }
    }
    // 触摸点不在subView,不处理点击事件
    return nil;
}

#pragma mark - setter
- (void)setAutoLayoutWithWeChatSytle:(BOOL)autoLayoutWithWeChatSytle
{
    _autoLayoutWithWeChatSytle = autoLayoutWithWeChatSytle;

    // 刷新
    self.photos = self.photos;
}

- (void)setImagesMaxCountWhenWillCompose:(NSInteger)imagesMaxCountWhenWillCompose
{
    _imagesMaxCountWhenWillCompose = imagesMaxCountWhenWillCompose;

    // 刷新图片个数
    self.images = self.images;
}

- (void)setLayoutType:(PYPhotosViewLayoutType)layoutType
{
    _layoutType = layoutType;

    // 刷新
    self.photosMaxCol = self.photosMaxCol > 0 ? self.photosMaxCol : PYPhotosMaxCol;
}

- (void)setPhotosMaxCol:(NSInteger)photosMaxCol
{
    // 设置了线性布局(photosMaxCol 再次设置也无效)
    if (self.layoutType == PYPhotosViewLayoutTypeLine) photosMaxCol = self.photos.count;

    _photosMaxCol = photosMaxCol;

    // 刷新
    if (self.photos.count) { // 已发布状态,有图片才刷新
        self.photos = self.photos;
    } else if (self.images.count) { // 未发布状态,刷新布局
        self.images = self.images;
    }
}

- (void)setPhotosUrl
{
    // 取出图片最多个数
    NSInteger maxPhotosCount = self.thumbnailUrls.count > self.originalUrls.count ? self.thumbnailUrls.count : self.originalUrls.count;
    NSMutableArray *photosM = [NSMutableArray array];
    for (NSInteger i = 0; i < maxPhotosCount; i++)
    {
        // 创建模型
        PYPhoto *photo = [[PYPhoto alloc] init];
        // 赋值
        photo.original_pic = i < self.originalUrls.count ? self.originalUrls[i] : nil;
        photo.thumbnail_pic = i < self.thumbnailUrls.count ? self.thumbnailUrls[i] : nil;
        // 添加模型
        [photosM addObject:photo];
    }
    // 刷新
    self.photos = photosM;
}

- (void)setOriginalUrls:(NSArray<NSString *> *)originalUrls
{
    _originalUrls = originalUrls;

    // 设置模型链接
    [self setPhotosUrl];
}

- (void)setThumbnailUrls:(NSArray<NSString *> *)thumbnailUrls
{
    _thumbnailUrls = thumbnailUrls;

    // 设置模型链接
    [self setPhotosUrl];
}

- (void)setPhotos:(NSArray<PYPhoto *> *)photos
{
    _photos = photos;
    // 设置图片状态
    self.photosState = PYPhotosViewStateDidCompose;
    // 移除添加图片按钮
    [self.addImageButton removeFromSuperview];

    NSInteger photoCount = self.photos.count;
    // 添加相应的图片
    while (self.subviews.count < photoCount) { // UIImageView不够,需要创建
        PYPhotoView *photoView = [[PYPhotoView alloc] init];
        photoView.photosView = self;
        photoView.photos = self.photos;
        [self addSubview:photoView];
    }

    // 设置图片
    for(int i = 0; i < self.subviews.count; i++)
    {
        PYPhotoView *photoView = self.subviews[i];

#if DEBUG
        NSCAssert(![photos isKindOfClass:[PYPhotoView class]], @"The photoView must be PYPhotoView Class");
#endif
        // 设置标记
        photoView.tag = i;
        photoView.photos = self.photos;
        if (i < photoCount) {
            photoView.hidden = NO;
            // 设置图片
            photoView.photo = self.photos[i];
        }else{
            photoView.hidden = YES;
        }
    }

    // 设置contentSize和 self.size
    // 取出size
    CGSize size = [self sizeWithPhotoCount:self.photos.count photosState:self.photosState];
    self.contentSize = size;
    CGFloat width = size.width + self.py_x > PYScreenW ? PYScreenW - self.py_x : size.width;
    self.py_size = CGSizeMake(width, size.height);
}

- (void)setImages:(NSMutableArray<UIImage *> *)images
{
    // 图片大于规定数字(取前九张)
    if (images.count > self.imagesMaxCountWhenWillCompose) {
        NSRange range = NSMakeRange(0, self.imagesMaxCountWhenWillCompose);
        NSIndexSet *set = [NSIndexSet indexSetWithIndexesInRange:range];
        images = [NSMutableArray arrayWithArray:[images objectsAtIndexes:set]];
    };

    _images = images;

    // 移除添加图片按钮
    [self.addImageButton removeFromSuperview];

    self.photosState = PYPhotosViewStateWillCompose;

    NSInteger imageCount = images.count;

    // 添加相应的图片
    [self.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
    while (self.subviews.count < imageCount) { // UIImageView不够,需要创建
        PYPhotoView *photoView = [[PYPhotoView alloc] init];
        photoView.photosView = self;
        [self addSubview:photoView];
    }

    // 设置图片
    for(int i = 0; i < self.subviews.count; i++)
    {
        PYPhotoView *photoView = self.subviews[i];
        // 设置标记
#if DEBUG
        NSCAssert([photoView isKindOfClass:[PYPhotoView class]], @"The photoView must be PYPhotoView Class");
#endif
        photoView.tag = i;
        photoView.images = images;
        if (i < imageCount) {
            photoView.hidden = NO;
            // 设置图片
            photoView.image = images[i];
        }else{
            photoView.hidden = YES;
        }
    }

    // 设置contentSize和 self.size
    // 取出size
    CGSize size = [self sizeWithPhotoCount:self.images.count photosState:self.photosState];
    self.contentSize = size;
    CGFloat width = size.width + self.py_x > PYScreenW ? PYScreenW - self.py_x : size.width;
    self.py_size = CGSizeMake(width, size.height);

    // 刷新
    [self layoutSubviews];
}

- (void)setPhotoWidth:(CGFloat)photoWidth
{
    _photoWidth = photoWidth;

    // 刷新self的size
    if (self.photosState == PYPhotosViewStateWillCompose) { // 未发布
        self.images = self.images;
    } else if (self.photosState == PYPhotosViewStateDidCompose) { // 已发布
        self.photos = self.photos;
    }

    // 刷新布局
    [self layoutSubviews];
}

- (void)setPhotoHeight:(CGFloat)photoHeight
{
    _photoHeight = photoHeight;

    // 刷新self的size
    if (self.photosState == PYPhotosViewStateWillCompose) { // 未发布
        self.images = self.images;
    } else if (self.photosState == PYPhotosViewStateDidCompose) { // 已发布
        self.photos = self.photos;
    }

    // 刷新布局
    [self layoutSubviews];
}

- (void)setX:(CGFloat)x
{
    CGRect frame = self.frame;
    frame.origin.x = x;
    self.frame = frame;

    // 记录x值
    self.originalX = x;
    CGFloat width = CGRectGetMaxX(self.frame) > PYScreenW ? PYScreenW - self.originalX : self.frame.size.width;
    self.py_size = CGSizeMake(width, self.py_height);
}

#pragma mark - handle image
/** 点击添加图片调用此方法 */
- (void)addImageDidClicked
{
    // 发出添加图片的通知
    NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
    userInfo[PYAddImageDidClickedNotification] = self.images;
    NSNotification *notifaction = [[NSNotification alloc] initWithName:PYAddImageDidClickedNotification object:nil userInfo:userInfo];
    [[NSNotificationCenter defaultCenter] postNotification:notifaction];

    if ([self.delegate respondsToSelector:@selector(photosView:didAddImageClickedWithImages:)]) {
        [self.delegate photosView:self didAddImageClickedWithImages:self.images];
    }
}

/** 添加图片按钮 */
- (UIButton *)addImageButton
{
    if (!_addImageButton) {
        UIButton *addImage = [[UIButton alloc] init];
        [addImage setBackgroundImage:PYAddImage forState:UIControlStateNormal];
        [addImage addTarget:self action:@selector(addImageDidClicked) forControlEvents:UIControlEventTouchUpInside];
        _addImageButton = addImage;
    }
    return _addImageButton;
}

/** 根据新的图片(未发布)刷新界面 */
- (void)reloadDataWithImages:(NSMutableArray<UIImage *> *)images
{
    [self setImages:images];
}

/** 根据图片个数和图片状态自动计算出大小 */
- (CGSize)sizeWithPhotoCount:(NSInteger)count photosState:(NSInteger)state
{
    NSInteger maxCount = 0; // 每行最多个数
    NSInteger cols = 0; // 列数
    NSInteger rows = 0; // 行数
    CGFloat photosViewW = 0;
    CGFloat photosViewH = 0;
    // 根据图片个数设置图片(最少一个)
    maxCount = self.photosMaxCol > 0 ? self.photosMaxCol : 1;
    if (state == PYPhotosViewStateDidCompose) { // 已经发布
        if (self.photos.count > 0 && self.layoutType == PYPhotosViewLayoutTypeFlow && self.autoLayoutWithWeChatSytle) {
           maxCount = count == 4 ? 2 : maxCount;
        }
    } else if (state == PYPhotosViewStateWillCompose){ // 未发布
        if (count < self.imagesMaxCountWhenWillCompose) count ++;
    }
    // 如果图片为一张,则图片的大小和photosView一致
    if (count == 1 && !CGSizeEqualToSize(self.bounds.size, CGSizeMake(self.photoMargin, self.photoMargin))) {
        return self.bounds.size;
    }
    cols = (count >= maxCount) ? maxCount : count;
    rows = (count + maxCount - 1) / maxCount;

    photosViewW = cols * self.photoWidth + (cols - 1) * self.photoMargin;
    photosViewH = rows * self.photoHeight + (rows - 1) * self.photoMargin;

    return CGSizeMake(photosViewW, photosViewH);
}

- (void)layoutSubviews
{
    [super layoutSubviews];

    // 取消内边距
    self.contentInset = UIEdgeInsetsZero;
    NSInteger photosCount = self.photos.count > 0 ?  self.photos.count : self.images.count;

    NSInteger maxCol = self.photosMaxCol;

    if (self.photos.count == 4 && self.layoutType == PYPhotosViewLayoutTypeFlow && self.photosState == PYPhotosViewStateDidCompose && self.autoLayoutWithWeChatSytle) {
        maxCol = 2;
    }
    // 当图片为一张时,图片大小和photosView一致
    if (self.photos.count == 1) {
        PYPhotoView *photoView = self.subviews[0];
        photoView.frame = self.bounds;
        self.contentSize = self.bounds.size;
        return;
    }
    // 调整图片位置
    for (int i = 0; i < photosCount; i++) {
        PYPhotoView *photoView = self.subviews[i];
        NSInteger col = i % maxCol;
        NSInteger row = i / maxCol;

        photoView.py_y = row * (self.photoHeight + self.photoMargin);
        photoView.py_x = col * (self.photoWidth + self.photoMargin);
        photoView.py_width = self.photoWidth;
        photoView.py_height = self.photoHeight;
    }
    if (self.images.count < self.imagesMaxCountWhenWillCompose && self.photosState == PYPhotosViewStateWillCompose) {
        [self addSubview:self.addImageButton];
        self.addImageButton.py_y = (self.images.count / maxCol) * (self.photoHeight + self.photoMargin);
        self.addImageButton.py_x = (self.images.count % maxCol) * (self.photoWidth + self.photoMargin);
        self.addImageButton.py_width = self.photoWidth;
        self.addImageButton.py_height = self.photoHeight;
        if (self.images.count == 0) { // 数组为空
            self.py_size = self.addImageButton.py_size;
        }
    }
}

@end
NS_ASSUME_NONNULL_END


text:
<?php

namespace app\models;

use Yii;

/**
 * This is the model class for table "parametroValores".
 *
 * @property integer $idParametroValores
 * @property string $nombre
 * @property string $valor
 * @property integer $fk_idImprentaParametros
 *
 * @property ParametroAcabado[] $parametroAcabados
 * @property ImprentaParametros $fkIdImprentaParametros
 */
class ParametroValores extends \yii\db\ActiveRecord
{
    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return 'parametroValores';
    }

    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['fk_idImprentaParametros'], 'required'],
            [['fk_idImprentaParametros'], 'integer'],
            [['nombre', 'valor'], 'string', 'max' => 100]
        ];
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels()
    {
        return [
            'idParametroValores' => 'Id Parametro Valores',
            'nombre' => 'Nombre',
            'valor' => 'Valor',
            'fk_idImprentaParametros' => 'Fk Id Imprenta Parametros',
        ];
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getParametroAcabados()
    {
        return $this->hasMany(ParametroAcabado::className(), ['fk_idParametrosValores' => 'idParametroValores']);
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getFkIdImprentaParametros()
    {
        return $this->hasOne(ImprentaParametros::className(), ['idImprentaParametros' => 'fk_idImprentaParametros']);
    }
}


text: require 'locomotive/engine'