text: /*
   * Copyright 2014 Google Inc.
   *
   * 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.
   */
  // Author: chenyu@google.com (Yu Chen), morlovich@google.com (Maks Orlovich)
  
  #ifndef NET_INSTAWEB_REWRITER_PUBLIC_MAKE_SHOW_ADS_ASYNC_FILTER_H_
  #define NET_INSTAWEB_REWRITER_PUBLIC_MAKE_SHOW_ADS_ASYNC_FILTER_H_
  
  #include "net/instaweb/rewriter/public/common_filter.h"
  #include "pagespeed/kernel/base/string.h"
  #include "pagespeed/opt/ads/show_ads_snippet_parser.h"
  #include "pagespeed/kernel/base/basictypes.h"
  
  namespace net_instaweb {
  
  class HtmlCharactersNode;
  class HtmlElement;
  class RewriteDriver;
  class Statistics;
  class Variable;
  
  // This filter converts from synchronous AdSense snippets (showads.js)
  // to async ones (adsbygoogle.js)
  class MakeShowAdsAsyncFilter : public CommonFilter {
   public:
    static const char kShowAdsSnippetsConverted[];
    static const char kShowAdsSnippetsNotConverted[];
    static const char kShowAdsApiReplacedForAsync[];
  
    explicit MakeShowAdsAsyncFilter(RewriteDriver* rewrite_driver);
    virtual ~MakeShowAdsAsyncFilter();
  
    static void InitStats(Statistics* statistics);
  
    // Overrides CommonFilter
    virtual void StartDocumentImpl();
    virtual void StartElementImpl(HtmlElement* element);
    virtual void EndElementImpl(HtmlElement* element);
  
    // Overrides HtmlFilter
    virtual const char* Name() const {
      return "MakeShowAdsAsyncFilter";
    }
    virtual void Characters(HtmlCharactersNode* characters);
  
   private:
    // Parses an element that has 'content'; the parsed attributes are stored
    // in 'parsed_attributes'. It returns true if the element is a showads element
    // that can be changed to be async. 'parsed_attributes' are
    // meaningful only when this method returns true.
    //
    // Note that this is in particular looking for the inline snippets that
    // set various google_ad_ variables, rather than <script src="showads">
    bool IsApplicableShowAds(
        const GoogleString& content,
        ShowAdsSnippetParser::AttributeMap* parsed_attributes) const;
  
    // Creates elements for an adsbygoogle snippet that is equivalent to element
    // 'show_ads_element', whose attributes are parsed in 'parsed_attributes', and
    // replace the 'show_ads_element' with the created elements.
    void ReplaceShowAdsWithAdsByGoogleElement(
        const ShowAdsSnippetParser::AttributeMap& parsed_attributes,
        HtmlElement* show_ads_elment);
    void ReplaceShowAdsApiCallWithAdsByGoogleApiCall(
        HtmlElement* show_ads_elment);
  
    // The current element if it is a script element, NULL otherwise.
    HtmlElement* current_script_element_;
    // Contents of 'current_script_element_'.
    GoogleString current_script_element_contents_;
  
    // These two variables ('has_ads_by_google_js_' and
    // 'num_pending_show_ads_push_replacements_') are used when replacing a
    // showads ad by an adsbygoogle ad. More details are given below.
    //
    // In order to display adsbygoogle ads in an HTML page, the required JS
    // (adsbygoogle.js) must be loaded at least once a page, that is, a <script>
    // with src pointing to adsbygoogle.js should be present in the page.
    // For each ad, there must be
    // - a valid adsbygoogle <ins> element and
    // - a <script> element with a snippet that calls adsbygoogle API.
    // The <script> element that calls adsbygoogle API must be after the <ins>
    // element; it does not need to be immediately after the <ins> element.
    //
    // In order to display a showads ad in an HTML page, there must be
    // - a valid showads data <script> element and
    // - a <script> element with a snippet that calls showads API.
    // The <script> element that calls showads API must be after the showads data
    // <script> element; it does not need to be immediately after the showads data
    // <script> element.
    //
    // In this class, we use 'has_ads_by_google_js_' to track whether a
    // <script> element with src pointing to adsbygoogle.js has been seen.
    //
    // Each time an applicable showads data <script> element is seen, it is
    // replaced with an adsbygoogle <ins> element. And if no <script> element with
    // src pointing to adsbygoogle.js has been seen, we will create one and
    // insert it to the page, and also set 'has_ads_by_google_js_' to true to
    // prevent us from the doing this more than once.
    bool has_ads_by_google_js_;
    // The number of <script> elements with a snippet that calls showads API and
    // - that are expected to be paired with a replaced showads data <script>
    //   element, and
    // - that has not been replaced by a <script> element with a snippet that
    //   calls adsbygoogle API.
    int32 num_pending_show_ads_api_call_replacements_;
  
    ShowAdsSnippetParser show_ads_snippet_parser_;
  
    // Statistics variables.
    Variable* show_ads_snippets_converted_count_;
    Variable* show_ads_snippets_not_converted_count_;
    Variable* show_ads_api_replaced_for_async_;
  
    DISALLOW_COPY_AND_ASSIGN(MakeShowAdsAsyncFilter);
  };
  
  }  // namespace net_instaweb
  
  #endif  // NET_INSTAWEB_REWRITER_PUBLIC_MAKE_SHOW_ADS_ASYNC_FILTER_H_
  