func (p *WebPage) FrameURL() (string, error) {
	var resp struct {
		Value string `json:"value"`
	}
	if err := p.ref.process.doJSON("POST", "/webpage/FrameURL", map[string]interface{}{"ref": p.ref.id}, &resp); err != nil {
		return "", err
	}
	return resp.Value, nil
}
FrameURL returns the URL of the current frame.
package phantomjs
import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"time"
)
var (
	ErrInjectionFailed = errors.New("injection failed")
)
const (
	ShiftKey = 0x02000000
	CtrlKey  = 0x04000000
	AltKey   = 0x08000000
	MetaKey  = 0x10000000
	Keypad   = 0x20000000
)
const (
	DefaultPort    = 20202
	DefaultBinPath = "phantomjs"
)
type Process struct {
	path string
	cmd  *exec.Cmd
	BinPath string
	Port int
	Stdout io.Writer
	Stderr io.Writer
}
func NewProcess() *Process {
	return &Process{
		BinPath: DefaultBinPath,
		Port:    DefaultPort,
		Stdout:  os.Stdout,
		Stderr:  os.Stderr,
	}
}
func (p *Process) Path() string {
	return p.path
}
func (p *Process) Open() error {
	if err := func() error {
		path, err := ioutil.TempDir("", "phantomjs-")
		if err != nil {
			return err
		}
		p.path = path
		scriptPath := filepath.Join(path, "shim.js")
		if err := ioutil.WriteFile(scriptPath, []byte(shim), 0600); err != nil {
			return err
		}
		cmd := exec.Command(p.BinPath, scriptPath)
		cmd.Env = []string{fmt.Sprintf("PORT=%d", p.Port)}
		cmd.Stdout = p.Stdout
		cmd.Stderr = p.Stderr
		if err := cmd.Start(); err != nil {
			return err
		}
		p.cmd = cmd
		if err := p.wait(); err != nil {
			return err
		}
		return nil
	}(); err != nil {
		p.Close()
		return err
	}
	return nil
}
func (p *Process) Close() (err error) {
	if p.cmd != nil {
		if e := p.cmd.Process.Kill(); e != nil && err == nil {
			err = e
		}
		p.cmd.Wait()
	}
	if p.path != "" {
		if e := os.RemoveAll(p.path); e != nil && err == nil {
			err = e
		}
	}
	return err
}
func (p *Process) URL() string {
	return fmt.Sprintf("http://localhost:%d", p.Port)
}
func (p *Process) wait() error {
	ticker := time.NewTicker(1000 * time.Millisecond)
	defer ticker.Stop()
	timer := time.NewTimer(30 * time.Second)
	defer ticker.Stop()
	for {
		select {
		case <-timer.C:
			return errors.New("timeout")
		case <-ticker.C:
			if err := p.ping(); err == nil {
				return nil
			}
		}
	}
}
func (p *Process) ping() error {
	resp, err := http.Get(p.URL() + "/ping")
	if err != nil {
		return err
	}
	resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("unexpected status: %d", resp.StatusCode)
	}
	return nil
}
func (p *Process) CreateWebPage() (*WebPage, error) {
	var resp struct {
		Ref refJSON `json:"ref"`
	}
	if err := p.doJSON("POST", "/webpage/Create", nil, &resp); err != nil {
		return nil, err
	}
	return &WebPage{ref: newRef(p, resp.Ref.ID)}, nil
}
func (p *Process) doJSON(method, path string, req, resp interface{}) error {
	var r io.Reader
	if req != nil {
		buf, err := json.Marshal(req)
		if err != nil {
			return err
		}
		r = bytes.NewReader(buf)
	}
	httpRequest, err := http.NewRequest(method, p.URL()+path, r)
	if err != nil {
		return err
	}
	httpResponse, err := http.DefaultClient.Do(httpRequest)
	if err != nil {
		return err
	}
	defer httpResponse.Body.Close()
	body, err := ioutil.ReadAll(httpResponse.Body)
	if err != nil {
		return err
	}
	if httpResponse.StatusCode == http.StatusNotFound {
		return fmt.Errorf("not found: %s", path)
	}
	var errResp errorResponse
	if err := json.Unmarshal(body, &errResp); err != nil {
		return errors.New("phantomjs.Process: " + string(body))
	} else if errResp.Error != "" {
		return errors.New(errResp.Error)
	}
	if resp != nil {
		if err := json.Unmarshal(body, resp); err != nil {
			return fmt.Errorf("unmarshal error: err=%s, body=%s", err, body)
		}
	}
	return nil
}
type errorResponse struct {
	Error string `json:"error"`
}
var DefaultProcess = NewProcess()
func CreateWebPage() (*WebPage, error) {
	return DefaultProcess.CreateWebPage()
}
type WebPage struct {
	ref *Ref
}
func (p *WebPage) Open(url string) error {
	req := map[string]interface{}{
		"ref": p.ref.id,
		"url": url,
	}
	var resp struct {
		Status string `json:"status"`
	}
	if err := p.ref.process.doJSON("POST", "/webpage/Open", req, &resp); err != nil {
		return err
	}
	if resp.Status != "success" {
		return errors.New("failed")
	}
	return nil
}
func (p *WebPage) CanGoBack() (bool, error) {
	var resp struct {
		Value bool `json:"value"`
	}
	if err := p.ref.process.doJSON("POST", "/webpage/CanGoBack", map[string]interface{}{"ref": p.ref.id}, &resp); err != nil {
		return false, err
	}
	return resp.Value, nil
}
func (p *WebPage) CanGoForward() (bool, error) {
	var resp struct {
		Value bool `json:"value"`
	}
	if err := p.ref.process.doJSON("POST", "/webpage/CanGoForward", map[string]interface{}{"ref": p.ref.id}, &resp); err != nil {
		return false, err
	}
	return resp.Value, nil
}
func (p *WebPage) ClipRect() (Rect, error) {
	var resp struct {
		Value rectJSON `json:"value"`
	}
	if err := p.ref.process.doJSON("POST", "/webpage/ClipRect", map[string]interface{}{"ref": p.ref.id}, &resp); err != nil {
		return Rect{}, err
	}
	return Rect{
		Top:    resp.Value.Top,
		Left:   resp.Value.Left,
		Width:  resp.Value.Width,
		Height: resp.Value.Height,
	}, nil
}
func (p *WebPage) SetClipRect(rect Rect) error {
	req := map[string]interface{}{
		"ref": p.ref.id,
		"rect": rectJSON{
			Top:    rect.Top,
			Left:   rect.Left,
			Width:  rect.Width,
			Height: rect.Height,
		},
	}
	return p.ref.process.doJSON("POST", "/webpage/SetClipRect", req, nil)
}
func (p *WebPage) Content() (string, error) {
	var resp struct {
		Value string `json:"value"`
	}
	if err := p.ref.process.doJSON("POST", "/webpage/Content", map[string]interface{}{"ref": p.ref.id}, &resp); err != nil {
		return "", err
	}
	return resp.Value, nil
}
func (p *WebPage) SetContent(content string) error {
	return p.ref.process.doJSON("POST", "/webpage/SetContent", map[string]interface{}{"ref": p.ref.id, "content": content}, nil)
}
func (p *WebPage) Cookies() ([]*http.Cookie, error) {
	var resp struct {
		Value []cookieJSON `json:"value"`
	}
	if err := p.ref.process.doJSON("POST", "/webpage/Cookies", map[string]interface{}{"ref": p.ref.id}, &resp); err != nil {
		return nil, err
	}
	a := make([]*http.Cookie, len(resp.Value))
	for i := range resp.Value {
		a[i] = decodeCookieJSON(resp.Value[i])
	}
	return a, nil
}
func (p *WebPage) SetCookies(cookies []*http.Cookie) error {
	a := make([]cookieJSON, len(cookies))
	for i := range cookies {
		a[i] = encodeCookieJSON(cookies[i])
	}
	req := map[string]interface{}{"ref": p.ref.id, "cookies": a}
	return p.ref.process.doJSON("POST", "/webpage/SetCookies", req, nil)
}
func (p *WebPage) CustomHeaders() (http.Header, error) {
	var resp struct {
		Value map[string]string `json:"value"`
	}
	if err := p.ref.process.doJSON("POST", "/webpage/CustomHeaders", map[string]interface{}{"ref": p.ref.id}, &resp); err != nil {
		return nil, err
	}
	hdr := make(http.Header)
	for key, value := range resp.Value {
		hdr.Set(key, value)
	}
	return hdr, nil
}
func (p *WebPage) SetCustomHeaders(header http.Header) error {
	m := make(map[string]string)
	for key := range header {
		m[key] = header.Get(key)
	}
	req := map[string]interface{}{"ref": p.ref.id, "headers": m}
	return p.ref.process.doJSON("POST", "/webpage/SetCustomHeaders", req, nil)
}
func (p *WebPage) FocusedFrameName() (string, error) {
	var resp struct {
		Value string `json:"value"`
	}
	if err := p.ref.process.doJSON("POST", "/webpage/FocusedFrameName", map[string]interface{}{"ref": p.ref.id}, &resp); err != nil {
		return "", err
	}
	return resp.Value, nil
}
func (p *WebPage) FrameContent() (string, error) {
	var resp struct {
		Value string `json:"value"`
	}
	if err := p.ref.process.doJSON("POST", "/webpage/FrameContent", map[string]interface{}{"ref": p.ref.id}, &resp); err != nil {
		return "", err
	}
	return resp.Value, nil
}
func (p *WebPage) SetFrameContent(content string) error {
	return p.ref.process.doJSON("POST", "/webpage/SetFrameContent", map[string]interface{}{"ref": p.ref.id, "content": content}, nil)
}
func (p *WebPage) FrameName() (string, error) {
	var resp struct {
		Value string `json:"value"`
	}
	if err := p.ref.process.doJSON("POST", "/webpage/FrameName", map[string]interface{}{"ref": p.ref.id}, &resp); err != nil {
		return "", err
	}
	return resp.Value, nil
}
func (p *WebPage) FramePlainText() (string, error) {
	var resp struct {
		Value string `json:"value"`
	}
	if err := p.ref.process.doJSON("POST", "/webpage/FramePlainText", map[string]interface{}{"ref": p.ref.id}, &resp); err != nil {
		return "", err
	}
	return resp.Value, nil
}
func (p *WebPage) FrameTitle() (string, error) {
	var resp struct {
		Value string `json:"value"`
	}
	if err := p.ref.process.doJSON("POST", "/webpage/FrameTitle", map[string]interface{}{"ref": p.ref.id}, &resp); err != nil {
		return "", err
	}
	return resp.Value, nil
}