/**
 * Helper: MatchManifest -> IWebBrowser2 -> Manifest -> location -> (styles . scripts)
 */
std::pair<wstringvector, wstringvector> 
CBrowserHelperObject::MatchManifest(IWebBrowser2 *webBrowser2,
                                    const Manifest::pointer& manifest, 
                                    const wstring& location)
{
    // Do nothing for special urls
    if (!(location.find(L"http:")  == 0 ||
          location.find(L"https:") == 0 ||
          location.find(L"file:")  == 0 /*||
		  location.find(L"about:") == 0*/)) {
        logger->debug(L"BrowserHelperObject::MatchManifest boring url"
                      L" -> " + location);        
        return std::pair<wstringvector, wstringvector>();
    }

    // is this a html document?
    CComPtr<IDispatch> disp;
    webBrowser2->get_Document(&disp);
    if (!disp) {
        logger->debug(L"BrowserHelperObject::MatchManifest "
                      L"no document");
        return std::pair<wstringvector, wstringvector>();
    }
    CComQIPtr<IHTMLDocument2, &IID_IHTMLDocument2> htmlDocument2(disp);
    if (!htmlDocument2) {
        logger->debug(L"BrowserHelperObject::MatchManifest "
                      L"not an IHTMLDocument2");
        return std::pair<wstringvector, wstringvector>();
    }    

    // is this an iframe ?
    wstring locationtop = location;
    bool is_not_iframe = m_webBrowser2.IsEqualObject(webBrowser2);
    if (is_not_iframe) {
    } else {
        CComPtr<IDispatch> disp_parent;
        HRESULT hr = webBrowser2->get_Parent(&disp_parent);
        if (FAILED(hr) || !disp_parent) {
            logger->debug(L"BrowserHelperObject::MatchManifest not an iframe 1"
                          L" -> " + location);
            return std::pair<wstringvector, wstringvector>();
        }
        CComQIPtr<IHTMLDocument2, &IID_IHTMLDocument2> htmlDocument2_parent(disp_parent);
        if (!htmlDocument2_parent) {
            logger->debug(L"BrowserHelperObject::MatchManifest not an iframe 2"
                          L" -> " + location);
            return std::pair<wstringvector, wstringvector>();
        }
        CComBSTR bstr;
        htmlDocument2_parent->get_URL(&bstr);
        locationtop = bstr;
    }

    // check content_scripts.matches, content_scripts.all_frames
    std::pair<wstringvector, wstringvector> ret;
    Manifest::ContentScripts::const_iterator script = manifest->content_scripts.begin();
    for (; script != manifest->content_scripts.end(); script++) {

		// check for exclude_matches
		bool exclude = false;
		wstringvector::const_iterator exclude_match = script->exclude_matches.begin();
		for (; exclude_match != script->exclude_matches.end(); exclude_match++) {
			if (wstring_match_wild(*exclude_match, location)) {
				logger->debug(L"BrowserHelperObject::MatchManifest exclude "
							  L" -> " + *exclude_match + L" -> " + location);
				exclude = true;
			}
		}

		// check for matches and add if, and only if there are no excludes for this activation
        wstringvector::const_iterator match = script->matches.begin();
        for (; match != script->matches.end(); match++) {
            bool matches = wstring_match_wild(*match, location);
            if ((matches && is_not_iframe && !exclude) ||        // matches top-level ||
                (matches && script->all_frames && !exclude)) {   // matches iframe  
                ret.first.insert(ret.first.end(), script->css.begin(), script->css.end());
                ret.second.insert(ret.second.end(), script->js.begin(), script->js.end());
            }
        }
    }

    return ret;
}
/**
 * Helper: MatchManifest -> IWebBrowser2 -> Manifest -> location -> (styles . scripts)
 */
std::pair<wstringvector, wstringvector> 
CBrowserHelperObject::MatchManifest(IWebBrowser2 *webBrowser2, const Manifest::pointer& manifest,  const wstring& location)
{
  CComPtr<IDispatch> disp = nullptr;
  CComPtr<IDispatch> disp_parent = nullptr;
  CComBSTR bstr = nullptr;
  std::pair<wstringvector, wstringvector> ret;
  HRESULT hr = S_OK;

  for (;;) {
    // Do nothing for special urls
    if (!(location.find(L"http:") == 0 ||
      location.find(L"https:") == 0 ||
      location.find(L"file:") == 0)) {
      logger->debug(L"BrowserHelperObject::MatchManifest boring url -> " + location);
      break;
    }

    // is this a html document?    
    hr = webBrowser2->get_Document(&disp);
    BreakOnNullWithErrorLog(disp, L"BrowserHelperObject::MatchManifest no document");
    BreakOnFailed(hr);
    
    CComQIPtr<IHTMLDocument2, &IID_IHTMLDocument2> htmlDocument2(disp);
    BreakOnNullWithErrorLog(htmlDocument2, L"BrowserHelperObject::MatchManifest not an IHTMLDocument2");

    // is this an iframe ?
    wstring locationtop = location;
    bool const is_not_iframe = m_webBrowser2.IsEqualObject(webBrowser2);
    if (!is_not_iframe) {
      hr = webBrowser2->get_Parent(&disp_parent);
      BreakOnNullWithErrorLog(disp_parent, L"BrowserHelperObject::MatchManifest not an iframe 1 -> " + location);
      BreakOnFailed(hr);

      CComQIPtr<IHTMLDocument2, &IID_IHTMLDocument2> htmlDocument2_parent(disp_parent);
      BreakOnNullWithErrorLog(htmlDocument2_parent, L"BrowserHelperObject::MatchManifest not an iframe 2 -> " + location);
      
      hr = htmlDocument2_parent->get_URL(&bstr);
      BreakOnFailed(hr);
      locationtop = bstr;
    }

    // check content_scripts.matches, content_scripts.all_frames
    for (auto& script : manifest->content_scripts) {
      // check for exclude_matches
      bool exclude = false;
      for (auto& exclude_match : script.exclude_matches) 
        if (wstring_match_wild(exclude_match, location)) {
          logger->debug(L"BrowserHelperObject::MatchManifest exclude -> " + exclude_match + L" -> " + location);
          exclude = true;
        }

      // check for matches and add if, and only if there are no excludes for this activation
      for (auto& match : script.matches) {
        bool matches = wstring_match_wild(match, location);
        if ( (is_not_iframe || script.all_frames) && matches && !exclude) {
          ret.first.insert(ret.first.end(), script.css.begin(), script.css.end());
          ret.second.insert(ret.second.end(), script.js.begin(), script.js.end());
        }
      }
    }
    
    break;
  }

  return ret;
}