public static function run()
 {
     /*
      * Normal request. Substitute the response with our own page.
      */
     if (!isset($_GET[self::QUERY_STRING_PARAM_NAME])) {
         $iframe_src = $_SERVER['REQUEST_URI'];
         if ($_SERVER['QUERY_STRING']) {
             $iframe_src .= '&';
         } else {
             $iframe_src .= '?';
         }
         $iframe_src .= self::QUERY_STRING_PARAM_NAME . '=' . self::OUTPUT_TYPE_IFRAME;
         $request_path_depth = count(explode('/', parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)));
         $script_path_depth = count(explode('/', $_SERVER['SCRIPT_NAME']));
         $rwb_path_relative_to_request_path = str_repeat('../', $request_path_depth - $script_path_depth) . 'rwb';
         require 'substitute-page.php';
         exit;
     }
     if (self::getOutputType() == self::OUTPUT_TYPE_JSONP) {
         // Output header now since other header output might block it later.
         header('Content-Type: application/javascript');
     }
     // Turn on output buffer to capture all output.
     ob_start();
     // Make this run after all output is completed.
     register_shutdown_function(function () {
         $html = ob_get_clean();
         RedirectWhenBlockedFull::injectBaseTag($html);
         /*
          * This request comes from another base url (mirror or source host).
          * We take the normal output and turn it into a jsonp response.
          */
         if (RedirectWhenBlockedFull::getOutputType() == RedirectWhenBlockedFull::OUTPUT_TYPE_JSONP) {
             print self::getJsonpCallbackName() . '(' . json_encode(array('html' => mb_convert_encoding_plus($html, 'UTF-8', RedirectWhenBlockedFull::getCharset($html)))) . ')';
         } else {
             print $html;
         }
     });
 }
 private function getBodyFilteredByContentType($body, $content_type)
 {
     switch ($content_type) {
         case self::CONTENT_TYPE_TEXT_CSS:
             $css_base_url = $this->getCssBaseUrl();
             // Convert protocol relative URLs to HTTPS.
             $body = str_replace('url(//', 'url(https://', $body);
             $body = str_replace('url("//', 'url("https://', $body);
             $body = str_replace('url(\'//', 'url(\'https://', $body);
             // Disabling this part. Not sure why it was added in the first place.
             /*
              * $body = str_replace('url("../', 'url("' . $css_base_url . '../',
              * $body);
              * $body = str_replace('url(\'../',
              * 'url(\'' . $css_base_url . '../', $body);
              * $body = str_replace('url(../', 'url(' . $css_base_url . '../',
              * $body);
              */
             $body = str_replace('url("/', 'url("' . $css_base_url, $body);
             $body = str_replace('url(\'/', 'url(\'' . $css_base_url, $body);
             $body = str_replace('url(/', 'url(' . $css_base_url, $body);
             break;
         case self::CONTENT_TYPE_TEXT_HTML:
             $dom = $this->getDomFromHtml($body);
             if ($dom) {
                 foreach ($dom->find('head title') as $title_element) {
                     $title = mb_convert_encoding_plus($title_element->text(), 'UTF-8', $this->getCharsetFromHeadersOrDom($dom));
                     if ($title) {
                         $title .= ' | 免翻墙镜像';
                         // Update title in DOM.
                         $title_element->innertext = mb_convert_encoding_plus($title, $this->getCharsetFromHeadersOrDom($dom), 'UTF-8');
                         break;
                     }
                 }
             }
             // Default title.
             if (!isset($title) || !$title) {
                 $title = '免翻墙镜像';
             }
             // Special - override meta redirects since RWB would just slow them down.
             if ($dom && count($dom->find('<META[http-equiv=refresh]'))) {
                 foreach ($dom->find('<META[http-equiv=refresh]') as $meta) {
                     $content = $meta->getAttribute('content');
                     if ($content) {
                         $content_chunks = explode(';', $content, 2);
                         if (isset($content_chunks[1])) {
                             $url_chunks = explode('=', $content_chunks[1], 2);
                             if (isset($url_chunks[1])) {
                                 $url = trim($url_chunks[1], '\'" ');
                                 if ($url) {
                                     $this->headers['Location'] = $url;
                                     return '';
                                 }
                             }
                         }
                     }
                 }
             }
             if ($this->shouldUseRedirectWhenBlocked()) {
                 RedirectWhenBlockedFull::setCharset($this->getCharsetFromHeadersOrDom($dom));
                 RedirectWhenBlockedFull::setWebsiteTitle($title);
                 RedirectWhenBlockedFull::run();
                 if (RedirectWhenBlockedFull::getOutputType() == RedirectWhenBlockedFull::OUTPUT_TYPE_JSONP) {
                     // RWB will turn output into JSONP. Remove content-type header to keep header sent by RWB.
                     if (isset($this->headers['Content-Type'])) {
                         unset($this->headers['Content-Type']);
                     }
                     // Also remove charset specification in inline HTML.
                     if ($dom) {
                         foreach ($dom->find('meta[http-equiv=Content-Type], meta[charset]') as $meta) {
                             $meta->outertext = '';
                         }
                     }
                 }
             }
             // Might be for example JSONP data served with an incorrect header.
             if (!$dom || !$dom->find('body')) {
                 break;
             }
             DomUrlFilters::applyAll($dom, $this->request);
             // Special for iframes. Prevent RWB from hijacking content (iframe in iframe problem).
             foreach ($dom->find('iframe') as $iframe) {
                 $src = $iframe->getAttribute('src');
                 if ($src) {
                     $src = http_add_query_component($src, RedirectWhenBlockedFull::QUERY_STRING_PARAM_NAME, RedirectWhenBlockedFull::OUTPUT_TYPE_IFRAME);
                     $iframe->setAttribute('src', $src);
                 }
             }
             // Special for style attributes - apply CSS filter.
             foreach ($dom->find('*[style]') as $style_element) {
                 $style_element->setAttribute('style', $this->getBodyFilteredByContentType($style_element->getAttribute('style'), self::CONTENT_TYPE_TEXT_CSS));
             }
             $dom_do_reset = false;
             // Don't insert stuff if request failed.
             if ($this->getResponseCode() == 200) {
                 if (Conf::$html_body_appendix) {
                     foreach ($dom->find('body') as $b) {
                         $b->innertext .= Conf::$html_body_appendix;
                         break;
                     }
                     $dom_do_reset = true;
                 }
                 if (Conf::$html_head_appendix) {
                     foreach ($dom->find('head') as $h) {
                         $h->innertext .= Conf::$html_head_appendix;
                         break;
                     }
                     $dom_do_reset = true;
                 }
             }
             if (count($dom->find('script[async]'))) {
                 if ($dom_do_reset) {
                     // Bug in Simple HTML Dom means that DOM has to be reloaded before further edits.
                     $this->resetDom($dom);
                     $dom_do_reset = false;
                 }
                 // Remove 'async' attributes because they break the page in IE11 when loaded in our iframe.
                 foreach ($dom->find('script[async]') as $x) {
                     $x->removeAttribute('async', '');
                 }
             }
             if (count($dom->find('style'))) {
                 if ($dom_do_reset) {
                     // Bug in Simple HTML Dom means that DOM has to be reloaded before further edits.
                     $this->resetDom($dom);
                     $dom_do_reset = false;
                 }
                 foreach ($dom->find('style') as $style) {
                     $style->innertext = $this->getBodyFilteredByContentType($style->innertext, self::CONTENT_TYPE_TEXT_CSS);
                 }
             }
             $body = $dom->__toString();
             $this->destroyDom($dom);
             break;
     }
     return $body;
 }