browser.js 17.6 KB
Newer Older
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
1 2
/**
 * Wechat for Bot. and for human who can talk with bot/robot
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
3
 *
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
4
 * Interface for puppet
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
5
 *
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
6 7 8 9
 * Licenst: ISC
 * https://github.com/zixia/wechaty
 *
 */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
10

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
11
const fs            = require('fs')
12
const co            = require('co')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
13
const path          = require('path')
14 15
const util          = require('util')
const EventEmitter  = require('events')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
16 17
const WebDriver     = require('selenium-webdriver')
const retryPromise  = require('retry-promise').default // https://github.com/olalonde/retry-promise
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
18

19
const log = require('../npmlog-env')
20

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
21
const Config  = require('../config')
22

23
class Browser extends EventEmitter {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
24 25

  constructor({
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
26
    head = process.env.WECHATY_HEAD || Config.DEFAULT_HEAD
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
27 28
    , sessionFile
  } = {}) {
29
    log.verbose('Browser', 'constructor() with head(%s) sessionFile(%s)', head, sessionFile)
30
    super()
31
    log.verbose('PuppetWebBrowser', 'constructor()')
32
    this.head = head
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
33
    this.sessionFile = sessionFile // a file to save session cookies
34 35

    this.live = false
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
36 37
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
38

39
  toString() { return `Browser({head:${this.head})` }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
40 41

  init() {
42
    return this.initDriver()
43 44 45 46
    .then(() => {
      this.live = true
      return this
    })
47 48 49
    .catch(e => {
      // XXX: must has a `.catch` here, or promise will hang! 2016/6/7
      // XXX: if no `.catch` here, promise will hang!
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
50
      // with selenium-webdriver v2.53.2
51 52 53 54
      // XXX: https://github.com/SeleniumHQ/selenium/issues/2233
      log.error('PuppetWebBrowser', 'init() exception: %s', e.message)
      throw e
    })
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
55 56
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
57
  open(url = 'https://wx.qq.com') {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
58 59 60 61 62 63 64 65 66 67 68
    log.verbose('PuppetWebBrowser', `open(${url})`)

    // TODO: set a timer to guard driver.get timeout, then retry 3 times 201607
    return this.driver.get(url)
    .catch(e => {
      log.error('PuppetWebBrowser', 'open() exception: %s', e.message)
      this.dead(e.message)
      throw e
    })
  }

69
  initDriver() {
70
    log.verbose('PuppetWebBrowser', 'initDriver(head: %s)', this.head)
71
    return new Promise((resolve, reject) => {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
72 73 74 75 76 77 78
      switch (true) {
        case !this.head: // no head default to phantomjs
        case /phantomjs/i.test(this.head):
        case /phantom/i.test(this.head):
          this.driver = this.getPhantomJsDriver()
          break
        case /firefox/i.test(this.head):
79 80 81 82
          this.driver = new WebDriver.Builder()
          .setAlertBehavior('ignore')
          .forBrowser('firefox')
          .build()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
83 84
          break
        case /chrome/i.test(this.head):
85
          this.driver = this.getChromeDriver()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
86 87
          break
        default: // unsupported browser head
88 89 90
          throw new Error('unsupported head: ' + this.head)
      }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
91 92 93
      // XXX: if no `setTimeout()` here, promise will hang forever!
      // with a confirmed bug in selenium-webdriver v2.53.2:
      // https://github.com/SeleniumHQ/selenium/issues/2233
94 95 96
      // FIXED: selenium v3 released 20160807
      // setTimeout(() => { resolve(this.driver) }, 0)
      resolve(this.driver)
97
    })
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
98
  }
99

100 101 102 103 104
  refresh() {
    log.verbose('PuppetWebBrowser', 'refresh()')
    return this.driver.navigate().refresh()
  }

105
  getChromeDriver() {
106 107 108
    const options = {
      args: ['--no-sandbox']
    }
109
    const customChrome = WebDriver.Capabilities.chrome()
110 111
                                  // .set('webdriver.chrome.args', '--no-sandbox')
                                  .set('chromeOptions', options)
112 113 114 115 116 117 118 119

    return new WebDriver.Builder()
                        .setAlertBehavior('ignore')
                        .forBrowser('chrome')
                        .withCapabilities(customChrome)
                        .build()
  }
 
120
  getPhantomJsDriver() {
121
    // setup custom phantomJS capability https://github.com/SeleniumHQ/selenium/issues/2069
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
122
    const phantomjsExe = require('phantomjs-prebuilt').path
123
    // const phantomjsExe = require('phantomjs2').path
124 125

    const phantomjsArgs = [
126 127 128
      '--load-images=false'
      , '--ignore-ssl-errors=true'  // this help socket.io connect with localhost
      , '--web-security=false'      // https://github.com/ariya/phantomjs/issues/12440#issuecomment-52155299
129 130
      //, '--ssl-protocol=TLSv1'    // https://github.com/ariya/phantomjs/issues/11239#issuecomment-42362211
      , '--ssl-protocol=any'        // http://stackoverflow.com/a/26503588/1123955
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
131 132 133 134 135

      // issue: Secure WebSocket(wss) do not work with Self Signed Certificate in PhantomJS #12 
      // , '--ssl-certificates-path=D:\\cygwin64\\home\\zixia\\git\\wechaty' // http://stackoverflow.com/a/32690349/1123955
      // , '--ssl-client-certificate-file=cert.pem' // 

136
    ]
137
    if (process.env.WECHATY_DEBUG) {
138 139 140
      phantomjsArgs.push('--remote-debugger-port=8080') // XXX: be careful when in production env.
      phantomjsArgs.push('--webdriver-loglevel=DEBUG')
      // phantomjsArgs.push('--webdriver-logfile=webdriver.debug.log')
141
    } else {
142 143 144 145 146
      if (log && log.level === 'silent') {
        phantomjsArgs.push('--webdriver-loglevel=NONE')
      } else {
        phantomjsArgs.push('--webdriver-loglevel=ERROR')
      }
147 148 149 150 151 152
    }

    const customPhantom = WebDriver.Capabilities.phantomjs()
    .setAlertBehavior('ignore')
    .set('phantomjs.binary.path', phantomjsExe)
    .set('phantomjs.cli.args', phantomjsArgs)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
153

154
    log.silly('PuppetWebBrowser', 'phantomjs binary: ' + phantomjsExe)
155
    log.silly('PuppetWebBrowser', 'phantomjs args: ' + phantomjsArgs.join(' '))
156

157
    const driver = new WebDriver.Builder()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
158 159
    .withCapabilities(customPhantom)
    .build()
160
    
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
		/**
		 *  ISSUE #21 - https://github.com/zixia/wechaty/issues/21
	 	 *
 	 	 *	http://phantomjs.org/api/webpage/handler/on-resource-requested.html
		 *	http://stackoverflow.com/a/29544970/1123955
		 *  https://github.com/geeeeeeeeek/electronic-wechat/pull/319
		 *
		 */
  	driver.executePhantomJS(`
this.onResourceRequested = function(request, net) {
   console.log('REQUEST ' + request.url);
   blockRe = /wx\.qq\.com\/\?t=v2\/fake/i
   if (blockRe.test(request.url)) {
       console.log('Abort ' + request.url);
       net.abort();
   }
}
`)

180 181 182 183
    // https://github.com/detro/ghostdriver/blob/f976007a431e634a3ca981eea743a2686ebed38e/src/session.js#L233
    // driver.manage().timeouts().pageLoadTimeout(2000)
    
    return driver
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
184
  }
185

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
186
  quit() {
187
    log.verbose('PuppetWebBrowser', 'quit()')
188
    this.live = false
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
189
    if (!this.driver) {
190
      log.verbose('PuppetWebBrowser', 'driver.quit() skipped because no driver')
191
      return Promise.resolve('no driver')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
192
    } else if (!this.driver.getSession()) {
193
      this.driver = null
194
      log.verbose('PuppetWebBrowser', 'driver.quit() skipped because no driver session')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
195
      return Promise.resolve('no driver session')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
196
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
197

198
    return co.call(this, function* () {
199
      log.silly('PuppetWebBrowser', 'quit() co()')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
200
      yield this.driver.close() // http://stackoverflow.com/a/32341885/1123955
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
201
      log.silly('PuppetWebBrowser', 'quit() driver.close()-ed')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
202
      yield this.driver.quit()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
203
      log.silly('PuppetWebBrowser', 'quit() driver.quit()-ed')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
204
      this.driver = null
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
205
      log.silly('PuppetWebBrowser', 'quit() this.driver = null')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
206

207 208 209 210 211 212
      /**
       * 
       * if we use AVA to test, then this.clean will cause problems 
       * because there will be more than one instance of browser with the same nodejs process id
       * 
       */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
213
      yield this.clean()
214

215
      log.silly('PuppetWebBrowser', 'quit() co() end')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
    }).catch(e => {
      // console.log(e)
      // log.warn('PuppetWebBrowser', 'err: %s %s %s %s', e.code, e.errno, e.syscall, e.message)
      log.error('PuppetWebBrowser', 'quit() exception: %s', e.message)

      const crashMsgs = [
        'ECONNREFUSED'
        , 'WebDriverError: .* not reachable'
        , 'NoSuchWindowError: no such window: target window already closed'
      ]
      const crashRegex = new RegExp(crashMsgs.join('|'), 'i')

      if (crashRegex.test(e.message)) { log.warn('PuppetWebBrowser', 'driver.quit() browser crashed') }
      else                            { log.warn('PuppetWebBrowser', 'driver.quit() exception: %s', e.message) }
    })
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
231
  }
232

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
233
  clean() {
234
    const max = 15
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
235
    const backoff = 100
236

237 238 239
    /**
     * max = (2*totalTime/backoff) ^ (1/2)
     * timeout = 45000 for {max: 30, backoff: 100}
240
     * timeout = 11250 for {max: 15, backoff: 100}
241
     */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
242
    const timeout = max * (backoff * max) / 2
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
243

244
    return retryPromise({ max: max, backoff: backoff }, attempt => {
245
      log.silly('PuppetWebBrowser', 'clean() retryPromise: attempt %s time for timeout %s'
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
246
        , attempt,  timeout)
247 248

      return new Promise((resolve, reject) => {
249 250 251
        this.getBrowserPids()
        .then(pids => {
          if (pids.length === 0) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
252
            log.verbose('PuppetWebBrowser', 'clean() retryPromise() resolved')
253
            resolve('clean() browser process not found, at attemp#' + attempt)
254
          } else {
255
            reject(new Error('clean() found browser process, not clean, dirty'))
256 257
          }
        })
258
        .catch(e => reject(e))
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
259 260
      })
    })
261
    .catch(e => {
262
      log.error('PuppetWebBrowser', 'retryPromise failed: %s', e.message)
263 264
      throw e
    })
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
265
  }
266

267
  getBrowserPids() {
268 269
    log.silly('Browser', 'getBrowserPids()')
    
270 271 272 273 274 275
    return new Promise((resolve, reject) => {
      require('ps-tree')(process.pid, (err, children) => {
        if (err) {
          reject(err)
          return
        }
276
        let browserRe
277 278 279 280 281

        switch (true) {
          case !this.head: // no head default to phantomjs
          case /phantomjs/i.test(this.head):
          case /phantom/i.test(this.head):
282 283 284
            browserRe = 'phantomjs'
            break

285 286 287 288 289
          case this.head: // head default to chrome
          case /chrome/i.test(this.head):
            browserRe = 'chrome(?!driver)'
            break

290 291 292 293
          default:
            log.warn('PuppetWebBrowser', 'getBrowserPids() for unsupported head: %s', this.head)
            browserRe = this.head
        }
294

295
        let matchRegex = new RegExp(browserRe, 'i')
296
        const pids = children.filter(child => {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
297
          log.silly('Browser', 'getBrowserPids() child: %s', JSON.stringify(child))
298 299
          // https://github.com/indexzero/ps-tree/issues/18
          return matchRegex.test('' + child.COMMAND + child.COMM)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
300 301
        }).map(child => child.PID)

302 303 304 305 306 307
        resolve(pids)
        return
      })
    })
  }

308 309 310 311 312 313 314
  /**
   * only wrap addCookies for convinience
   *
   * use this.driver.manage() to call other functions like:
   * deleteCookie / getCookie / getCookies
   */
  addCookies(cookie) {
315
    if (this.dead()) { return Promise.reject(new Error('addCookies() - browser dead'))}
316

317
    if (typeof cookie.map === 'function') {
318
      return cookie.map(c => {
319
        return this.addCookies(c)
320 321
      })
    }
322 323 324 325 326 327
    /**
     * convert expiry from seconds to milliseconds. https://github.com/SeleniumHQ/selenium/issues/2245
     * with selenium-webdriver v2.53.2
     * NOTICE: the lastest branch of selenium-webdriver for js has changed the interface of addCookie:
     * https://github.com/SeleniumHQ/selenium/commit/02f407976ca1d516826990f11aca7de3c16ba576
     */
328
    // if (cookie.expiry) { cookie.expiry = cookie.expiry * 1000 /* XXX: be aware of new version of webdriver */}
329

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
330
    log.silly('PuppetWebBrowser', 'addCookies(%s)', JSON.stringify(cookie))
331 332

    return this.driver.manage()
333 334 335 336 337
    // this is old webdriver format
    // .addCookie(cookie.name, cookie.value, cookie.path
    //   , cookie.domain, cookie.secure, cookie.expiry)
    // thisi is new webdriver format
    .addCookie(cookie)
338
    .catch(e => {
339
      log.warn('PuppetWebBrowser', 'addCookies() exception: %s', e.message)
340 341
      throw e
    })
342
  }
343 344

  execute(script, ...args) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
345
    log.silly('PuppetWebBrowser', `Browser.execute(${script.slice(0, 80)})`)
346
    // log.verbose('PuppetWebBrowser', `Browser.execute() driver.getSession: %s`, util.inspect(this.driver.getSession()))
347
    if (this.dead()) { return Promise.reject(new Error('browser dead')) }
348 349 350

    return this.driver.executeScript.apply(this.driver, arguments)
    .catch(e => {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
351 352
      // this.dead(e)
      log.warn('PuppetWebBrowser', 'execute() exception: %s', e.message)
353 354 355 356
      throw e
    })
  }

357 358 359 360 361
  /**
   *
   * check whether browser is full functional
   *
   */
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
  readyLive() {
    log.verbose('PuppetWebBrowser', 'readyLive()')
    if (this.dead()) {
      return Promise.reject(new Error('this.dead() true'))
    }
    return new Promise((resolve, reject) => {
      this.execute('return 1+1')
      .then(r => {
        if (r === 2) {
          resolve(true) // browser ok, living
          return
        }
        const errMsg = 'deadEx() found dead browser coz 1+1 = ' + r + ' (not 2)'
        log.verbose('PuppetWebBrowser', errMsg)
        this.dead(errMsg)
        reject(new Error(errMsg)) // browser not ok, dead
        return
      })
      .catch(e => {
        const errMsg = 'deadEx() found dead browser coz 1+1 = ' + e.message
        log.verbose('PuppetWebBrowser', errMsg)
        this.dead(errMsg)
        reject(new Error(errMsg)) // browser not live
        return
      })
    })
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
389

390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
  dead(forceReason) {
    let errMsg
    let dead = false

    if (forceReason) {
      dead = true
      errMsg = forceReason
    } else if (!this.live) {
      dead = true
      errMsg = 'browser not live'
    } else if (!this.driver || !this.driver.getSession()) {
      dead = true
      errMsg = 'no driver or session'
    }

    if (dead) {
406
      log.warn('PuppetWebBrowser', 'dead() because %s', errMsg)
407
      this.live = false
408
      // must use nextTick here, or promise will hang... 2016/6/10
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
409 410
      process.nextTick(_ => {
        log.verbose('PuppetWebBrowser', 'dead() emit a `dead` event because %s', errMsg)
411 412 413 414 415 416
        this.emit('dead', errMsg)
      })
    }
    return dead
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
417 418 419 420
  checkSession() {
    // just check cookies, no file operation
    log.verbose('PuppetWebBrowser', 'checkSession()')

421
    if (this.dead()) { return Promise.reject(new Error('checkSession() - browser dead'))}
422 423 424 425

    return this.driver.manage().getCookies()
    .then(cookies => {
      // log.silly('PuppetWeb', 'checkSession %s', require('util').inspect(cookies.map(c => { return {name: c.name/*, value: c.value, expiresType: typeof c.expires, expires: c.expires*/} })))
426
      log.silly('PuppetWebBrowser', 'checkSession %s', cookies.map(c => c.name).join(','))
427 428
      return cookies
    })
429 430 431 432
    .catch(e => {
      log.error('PuppetWebBrowser', 'checkSession() getCookies() exception: %s', e.message)
      throw e
    })
433 434
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
435 436 437 438 439 440
  cleanSession() {
    log.verbose('PuppetWebBrowser', `cleanSession(${this.sessionFile})`)
    if (!this.sessionFile) {
      return Promise.reject(new Error('cleanSession() no session'))
    }

441
    if (this.dead())  { return Promise.reject(new Error('cleanSession() - browser dead'))}
442

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
443
    const filename = this.sessionFile
444 445 446
    return new Promise((resolve, reject) => {
      require('fs').unlink(filename, err => {
        if (err && err.code!=='ENOENT') {
447
          log.silly('PuppetWebBrowser', 'cleanSession() unlink session file %s fail: %s', filename, err.message)
448 449 450 451 452 453
        }
        resolve()
      })
    })
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
454 455 456 457 458 459 460 461 462
  saveSession() {
    log.silly('PuppetWebBrowser', `saveSession(${this.sessionFile})`)
    if (!this.sessionFile) {
      return Promise.reject(new Error('saveSession() no session'))
    } else if (this.dead()) {
      return Promise.reject(new Error('saveSession() - browser dead'))
    }

    const filename = this.sessionFile
463

464 465 466 467 468 469 470 471 472 473 474 475 476 477
    function cookieFilter(cookies) {
      const skipNames = [
        'ChromeDriver'
        , 'MM_WX_SOUND_STATE'
        , 'MM_WX_NOTIFY_STATE'
      ]
      const skipNamesRegex = new RegExp(skipNames.join('|'), 'i')
      return cookies.filter(c => {
        if (skipNamesRegex.test(c.name)) { return false }
        // else if (!/wx\.qq\.com/i.test(c.domain))  { return false }
        else                             { return true }
      })
    }
  
478 479
    return new Promise((resolve, reject) => {
      this.driver.manage().getCookies()
480 481
      .then(cookieFilter)
      .then(cookies => {
482 483
        // log.silly('PuppetWeb', 'saving %d cookies for session: %s', cookies.length
        //   , util.inspect(cookies.map(c => { return {name: c.name /*, value: c.value, expiresType: typeof c.expires, expires: c.expires*/} })))
484
        log.silly('PuppetWebBrowser', 'saving %d cookies for session: %s', cookies.length, cookies.map(c => c.name).join(','))
485 486 487 488

        const jsonStr = JSON.stringify(cookies)
        fs.writeFile(filename, jsonStr, function(err) {
          if(err) {
489
            log.error('PuppetWebBrowser', 'saveSession() fail to write file %s: %s', filename, err.Error)
490 491
            return reject(err)
          }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
492
          log.silly('PuppetWebBrowser', 'saved session(%d cookies) to %s', cookies.length, filename)
493
          return resolve(cookies)
494 495 496 497 498
        })
      })
      .catch(e => {
        log.error('PuppetWebBrowser', 'saveSession() getCookies() exception: %s', e.message)
        reject(e)
499 500 501 502
      })
    })
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
503 504 505 506 507 508 509
  loadSession() {
    log.verbose('PuppetWebBrowser', `loadSession(${this.sessionFile})`)
    if (!this.sessionFile) {
      return Promise.reject(new Error('loadSession() no sessionFile'))
    } else if (this.dead()) {
      return Promise.reject(new Error('loadSession() - browser dead'))
    }
510

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
511
    const filename = this.sessionFile
512 513 514 515

    return new Promise((resolve, reject) => {
      fs.readFile(filename, (err, jsonStr) => {
        if (err) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
516
          if (err) { log.silly('PuppetWebBrowser', 'loadSession(%s) skipped because error code: %s', filename, err.code) }
517
          return reject(new Error('error code:' + err.code))
518 519 520 521
        }
        const cookies = JSON.parse(jsonStr)

        const ps = this.addCookies(cookies)
522
        Promise.all(ps)
523
        .then(() => {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
524
          log.verbose('PuppetWebBrowser', 'loaded session(%d cookies) from %s', cookies.length, filename)
525 526 527
          resolve(cookies)
        })
        .catch(e => {
528
          log.error('PuppetWebBrowser', 'loadSession() addCookies() exception: %s', e.message)
529 530
          reject(e)
        })
531 532 533
      })
    })
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
534 535 536
}

module.exports = Browser